agora inbox for [email protected]  
help / color / mirror / Atom feed
PostgreSQL and Real Application Testing (RAT)
289+ messages / 5 participants
[nested] [flat]

* PostgreSQL and Real Application Testing (RAT)
@ 2019-08-27 10:47  ROS Didier <[email protected]>
  0 siblings, 3 replies; 289+ messages in thread

From: ROS Didier @ 2019-08-27 10:47 UTC (permalink / raw)
  To: [email protected] <[email protected]>

Hi

In my business, one of the things blocking the migration from Oracle to PostgreSQL is not having the equivalent of Oracle Real Application Testing .
This product captures a charge in production and replay it in a test environment.
this allows to know the impacts of a migration to a newer version, the creation of an index..
is there an equivalent in the PostgreSQL community?
if not, do you think it's technically possible to do it ?
who would be interested in this project ?

Thanks in advance
Best Regards

Didier ROS
EDF






Ce message et toutes les pièces jointes (ci-après le 'Message') sont établis à l'intention exclusive des destinataires et les informations qui y figurent sont strictement confidentielles. Toute utilisation de ce Message non conforme à sa destination, toute diffusion ou toute publication totale ou partielle, est interdite sauf autorisation expresse.

Si vous n'êtes pas le destinataire de ce Message, il vous est interdit de le copier, de le faire suivre, de le divulguer ou d'en utiliser tout ou partie. Si vous avez reçu ce Message par erreur, merci de le supprimer de votre système, ainsi que toutes ses copies, et de n'en garder aucune trace sur quelque support que ce soit. Nous vous remercions également d'en avertir immédiatement l'expéditeur par retour du message.

Il est impossible de garantir que les communications par messagerie électronique arrivent en temps utile, sont sécurisées ou dénuées de toute erreur ou virus.
____________________________________________________

This message and any attachments (the 'Message') are intended solely for the addressees. The information contained in this Message is confidential. Any use of information contained in this Message not in accord with its purpose, any dissemination or disclosure, either whole or partial, is prohibited except formal approval.

If you are not the addressee, you may not copy, forward, disclose or use any part of it. If you have received this message in error, please delete it and all copies from your system and notify the sender immediately by return message.

E-mail communication cannot be guaranteed to be timely secure, error or virus-free.


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

* Re: PostgreSQL and Real Application Testing (RAT)
@ 2019-08-27 13:49  Thomas Kellerer <[email protected]>
  parent: ROS Didier <[email protected]>
  2 siblings, 0 replies; 289+ messages in thread

From: Thomas Kellerer @ 2019-08-27 13:49 UTC (permalink / raw)
  To: [email protected]

ROS Didier schrieb am 27.08.2019 um 12:47:
> In my business, one of the things blocking the migration from Oracle
> to PostgreSQL is not having the equivalent of Oracle Real Application
> Testing .
>
> This product captures a charge in production and replay it in a test
> environment.
>
> this allows to know the impacts of a migration to a newer version,
> the creation of an index..
>
> is there an equivalent in the PostgreSQL community?
>
> if not, do you think it's technically possible to do it?
>
> who would be interested in this project?
Not sure how up-to-date that is, but you might want to have a look here:

https://wiki.postgresql.org/wiki/Statement_Playback






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

* Re: PostgreSQL and Real Application Testing (RAT)
@ 2019-08-27 18:42  Jaime Casanova <[email protected]>
  parent: ROS Didier <[email protected]>
  2 siblings, 0 replies; 289+ messages in thread

From: Jaime Casanova @ 2019-08-27 18:42 UTC (permalink / raw)
  To: ROS Didier <[email protected]>; +Cc: [email protected] <[email protected]>

On Tue, 27 Aug 2019 at 05:47, ROS Didier <[email protected]> wrote:

> Hi
>
>
>
> In my business, one of the things blocking the migration from Oracle to
> PostgreSQL is not having the equivalent of Oracle Real Application Testing .
>
> This product captures a charge in production and replay it in a test
> environment.
>
> this allows to know the impacts of a migration to a newer version, the
> creation of an index..
>
> is there an equivalent in the PostgreSQL community?
>

I used https://github.com/laurenz/pgreplay recently to re-execute the
queries sent to a pg9.1 in a pg11. It was very useful to find queries that
are affected but changes in default values of GUCs.

Normally, a query that works in an old version will work in a new one; but
this is useful to catch the few that don't if any

-- 
Jaime Casanova                      www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services


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

* Re: PostgreSQL and Real Application Testing (RAT)
@ 2019-08-28 00:32  Nikolay Samokhvalov <[email protected]>
  parent: ROS Didier <[email protected]>
  2 siblings, 1 reply; 289+ messages in thread

From: Nikolay Samokhvalov @ 2019-08-28 00:32 UTC (permalink / raw)
  To: ROS Didier <[email protected]>; +Cc: [email protected] <[email protected]>

On Tue, Aug 27, 2019 at 3:47 AM ROS Didier <[email protected]> wrote:

> Hi
>
>
>
> In my business, one of the things blocking the migration from Oracle to
> PostgreSQL is not having the equivalent of Oracle Real Application Testing .
>
> This product captures a charge in production and replay it in a test
> environment.
>
> this allows to know the impacts of a migration to a newer version, the
> creation of an index..
>
> is there an equivalent in the PostgreSQL community?
>
> if not, do you think it's technically possible to do it ?
>
> who would be interested in this project ?
>

Replaying workload might or might not apply well to your case.

There are several major difficulties if you want to replay workload:

1) How to "record" workload. You need to write all your queries to the
Postgres log. Three problems here:
  1a) pgreplay expects log_statements = 'all' while you might prefer
dealing with log_min_duration_statement instead. This is a minor issue
though, quite easy to solve with preprocessing.
  1b) under heavy load, log_min_duration_statement = 0 (or log_statements =
'all') will lead to performance degradation or even downtime. Possible
solutions are: write to memory, or don't write at all but send over the
network.
  1c) ideally, recoding just queries is not enough. To replay workload "as
is", we need to replay queries with known plans. There is no easy solution
to this problem in the Postgres ecosystem yet.

A couple of additional points regarding item 1b and 1c. In Postgres 12,
there is a cool new capability: sampling for query logging,
implemented by Adrien
Nayrat https://commitfest.postgresql.org/20/1691/  WIth this, it will be
possible to fully log, say, 5% of all transactions and use it for
replaying. Moreover, with auto_explain, it will be possible to have plans!
Open questions are: (a) how to determine, if N% is enough, and (b) how to
replay with specified plans. [If anyone is interested in working in this
direction – please reach out to me.]

2) Issues with replaying itself. I can highlight at least two problems here:
  2a) pgreplay might be not enough for your workload, it doesn't scale
well. If interested, look at its analog written in Go,
https://github.com/gocardless/pgreplay-go, but this is quite a young
project.
  2b) Postgres logs have millisecond precision (if you switched from %t to
%m in log_line_prefix), this might be not enough. There is a patch to
microsecond precision from David Fetter
https://www.postgresql.org/message-id/flat/20181023185050.GE6049%40fetter.org,
but that conversation hasn't yet led to commit.

Another approach you might be interested in -- workload simulation. This is
what we (Postgres.ai) now used in most times when building "lab"
environments for our clients. The idea is as follows:
- carefully analyze workload using pg_stat_statements (here, our
open-source tool called "postgres-checkup"
https://gitlab.com/postgres-ai/postgres-checkup might be helpful, see
reports in section K),
- take the most resource-consuming query groups (Top-N ordered by
total_time),
- create a set of files with statements with randomly filled parameters
(won't work for most cases, I discuss restrictions below),
- use pgbench, feed workload files to it, using multiple -f options, with
balancing (-f filename@XX, where XX is to be taked from
pg_statements_analysis, but this time, "calls" and their ratio in the whole
workload will be needed -- again, postgres-checkup can help here).
- run, analyze, compare behavior.

Restrictions of this approach are obvious:
- doesn't work well if most of your transactions have multiple statements,
- in many cases, randomization is hard (not obvious how to organize;
synthetic approach is far from real data distribution in storage and
workload; etc),
- the approach requires a significant amount of manual efforts.

However, the "workload simulation" approach is an extremely helpful
approach in many cases, helping with change management. It doesn't require
anything that might negatively affect your production workload, it utilizes
pgbench (or any other tool) which is reliable, has great features and
scales well.

You might be interested in looking at our tool that we built to conduct a
huge amount of DB experiments, Nancy CLI
https://gitlab.com/postgres-ai/nancy. It supports both "workload replay"
method (with pgreplay) and "workload simulation" (with pgbench). PM me if
you're interested in discussing details.

Thanks,
Nik


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

* Re: PostgreSQL and Real Application Testing (RAT)
@ 2019-08-28 00:56  Jaime Casanova <[email protected]>
  parent: Nikolay Samokhvalov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Jaime Casanova @ 2019-08-28 00:56 UTC (permalink / raw)
  To: Nikolay Samokhvalov <[email protected]>; +Cc: ROS Didier <[email protected]>; [email protected] <[email protected]>

On Tue, 27 Aug 2019 at 19:33, Nikolay Samokhvalov <[email protected]>
wrote:

> On Tue, Aug 27, 2019 at 3:47 AM ROS Didier <[email protected]> wrote:
>
>> Hi
>>
>>
>>
>> In my business, one of the things blocking the migration from Oracle to
>> PostgreSQL is not having the equivalent of Oracle Real Application Testing .
>>
>> This product captures a charge in production and replay it in a test
>> environment.
>>
>> this allows to know the impacts of a migration to a newer version, the
>> creation of an index..
>>
>> is there an equivalent in the PostgreSQL community?
>>
>> if not, do you think it's technically possible to do it ?
>>
>> who would be interested in this project ?
>>
>
> Replaying workload might or might not apply well to your case.
>
> There are several major difficulties if you want to replay workload:
>
> 1) How to "record" workload. You need to write all your queries to the
> Postgres log. Three problems here:
>   1a) pgreplay expects log_statements = 'all' while you might prefer
> dealing with log_min_duration_statement instead. This is a minor issue
> though, quite easy to solve with preprocessing.
>   1b) under heavy load, log_min_duration_statement = 0 (or log_statements
> = 'all') will lead to performance degradation or even downtime. Possible
> solutions are: write to memory, or don't write at all but send over the
> network.
>   1c) ideally, recoding just queries is not enough. To replay workload "as
> is", we need to replay queries with known plans. There is no easy solution
> to this problem in the Postgres ecosystem yet.
>
>
why? i prefer queries to take advantage of new plans for example if i'm
migrating from 9.5 to 9.6+ i would prefer that, when replaying, the queries
use parallel plans so i quickly get if that would somehow be a problem (for
example by using more cpu than before)

-- 
Jaime Casanova                      www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services


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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 289+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 289+ messages in thread


end of thread, other threads:[~2026-02-19 15:33 UTC | newest]

Thread overview: 289+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2019-08-27 10:47 PostgreSQL and Real Application Testing (RAT) ROS Didier <[email protected]>
2019-08-27 13:49 ` Thomas Kellerer <[email protected]>
2019-08-27 18:42 ` Jaime Casanova <[email protected]>
2019-08-28 00:32 ` Nikolay Samokhvalov <[email protected]>
2019-08-28 00:56   ` Jaime Casanova <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox