Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1w3F67-0012XI-28 for pgsql-hackers@arkaria.postgresql.org; Thu, 19 Mar 2026 15:15:43 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1w3F66-000gzr-0M for pgsql-hackers@arkaria.postgresql.org; Thu, 19 Mar 2026 15:15:42 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from <9erthalion6@gmail.com>) id 1w3F65-000gzj-27 for pgsql-hackers@lists.postgresql.org; Thu, 19 Mar 2026 15:15:42 +0000 Received: from mail-ej1-x62d.google.com ([2a00:1450:4864:20::62d]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from <9erthalion6@gmail.com>) id 1w3F63-000000001BM-3xEq for pgsql-hackers@postgresql.org; Thu, 19 Mar 2026 15:15:41 +0000 Received: by mail-ej1-x62d.google.com with SMTP id a640c23a62f3a-b980785a0bfso144775766b.3 for ; Thu, 19 Mar 2026 08:15:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1773933339; x=1774538139; darn=postgresql.org; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:from:to:cc:subject:date:message-id:reply-to; bh=CsDxBSCz9J239tT5/ouQncYA7DUWrIoU6kPlY1r9nbY=; b=S+lNbavYOvMe7qZDL6sqs2JtPM1qHD6wP3twLaSJ9sWK9jQCkPETMngE15CG0MBq5I osj/X1R42wTiOQvgnOE/ZDTT6zwyfeBYAJk8LRXd5d6Md5WsrVV3HVyNfAJm1GNkh/9h QFT8IlqcmXBVSg8ZOT0q7czKNhHKXwQS5h4wKXdT/QX+G5UTS5JXlUvm6pY6fxF6qs/w YS/hYo00p3bLO4v6ngDpkEGFXh3aT+P0YDtsqbXIgvJ28cOfBwCVkZHCNul/mxlw4UX6 QyXIgnhOOJ2hgVtL7osIp1G1P4rKv+Fl50a9mOs2cEbO6mg5KLbS0UPhwHLPcd90BMzM S7sQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773933339; x=1774538139; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:x-gm-gg:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=CsDxBSCz9J239tT5/ouQncYA7DUWrIoU6kPlY1r9nbY=; b=W9K5v91iXS7w3LFnEQ1lejMJdQewIDc7U8VZ4D97xliLFEzIZ6FR2Q5CGxRUbbfXLW gNjXkk6qUjHvCTBZ7HUn4OTh3itgAkTOZJuWTbpPaoLlq+81LZDKTZM58Q+X39WpeVKP /V2j04mWLUiOo+JJlXHe1pGvsuIxVvdFcHUBjuYTn1xSO4/VPZG6dAf/Zz8xpN1AuN9Q QWSId1KTCn95cxq7akU6pIHiiScUxIDWtiWmJx7LS9AuTxGvQw+0smCmCbTrMtBKyM3N Vvd6WWuADeRJu34oGOtyMMq/HMMvb4ltNtguf19qEgsDodjtZTrcMmTFsyUWwwJwGhy9 ggGw== X-Forwarded-Encrypted: i=1; AJvYcCVtuKdABQ6VCawYWxdgisgD2fKzy8QaJjJds2BrOIW+Cjcb+jNSS1C8+O90u+OrWfrzTIcjlOcahYVb/AY8@postgresql.org X-Gm-Message-State: AOJu0YyzKOMwYc2WbOPny0gWLvWPLKrUiR6ZYV0/Z0wdcFAnX9/5dtLO 2C5DIExVypiq8cbNwce4WnNqTfjtpmOhTLKZSHuj5F0G96MCdkU3ImQJ X-Gm-Gg: ATEYQzyO0GBQ4lNOmw8rx1StkHR2BKMBp8dzB0R8Bg+oDzknic89SdeiN3Oj+q0NR76 2hmfAxF88G0oJ7ECjjBqCi0m85j6Y6fQgB66rKjI0vXebS0yj6tFDv3dct78NHPZjPfyKuwTa9r w8lYTh502WxfopmKFD+R3durw/xZ+who98i254SHwo0zWXxMfLxc6ZifBG/o0ohJIpREjYjfe42 DNOOauaxcOTk/Sr1A6cnGHYdmnt0ZWjsa3/vAHnguL/VN8ZCzYcriPZ9fTV4gnsXWuH7LzclQPI N3ohsrornmnmgLkNiqVguY9539poVFZA+phZ0pSE2LgqDxbnsVOnftEblW6L5+BFNpSPNXlhEcp wsU6vHZpbCHHaaEZdoWvdq6zU+AQKx+pPFa0ORiEIrrwmRloPFqm8vkKcKaksdKlFSNwwNvjAM1 ZFdT2lcHzjdDyRZ8Bnfw6uYl1eZ5MOcXBW0o+1OLysaUFmXssHw0ttIexgTudyE8yZ21RE8yxmA Um//bk3kZKLIsxwMC9tOAJeTQ66HU2SplQBtg== X-Received: by 2002:a17:907:e115:b0:b97:b84a:e97c with SMTP id a640c23a62f3a-b97f4972d29mr302209666b.30.1773933338265; Thu, 19 Mar 2026 08:15:38 -0700 (PDT) Received: from ddolgov-thinkpadt14sgen1.rmtde.csb (dslb-002-202-135-061.002.202.pools.vodafone-ip.de. [2.202.135.61]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-b97f1689e66sm467534366b.36.2026.03.19.08.15.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 19 Mar 2026 08:15:37 -0700 (PDT) Date: Thu, 19 Mar 2026 16:15:35 +0100 From: Dmitry Dolgov <9erthalion6@gmail.com> To: Jacob Champion Cc: Daniel Gustafsson , PostgreSQL Hackers Subject: Re: Add ssl_(supported|shared)_groups to sslinfo Message-ID: References: MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="eajje57lwcd22geu" Content-Disposition: inline In-Reply-To: List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --eajje57lwcd22geu Content-Type: text/plain; charset=us-ascii Content-Disposition: inline > On Wed, Mar 18, 2026 at 12:18:52PM +0100, Dmitry Dolgov wrote: > Added those into the documentation, will create a CF item. And had to fix one thing right away, the installable version was missing. --eajje57lwcd22geu Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0001-contrib-sslinfo-Add-ssl_group_info.patch" From 65b6abbac2ab18aad0a64bc5e9cd8e9f7b26b797 Mon Sep 17 00:00:00 2001 From: Dmitrii Dolgov <9erthalion6@gmail.com> Date: Thu, 19 Feb 2026 16:33:17 +0100 Subject: [PATCH v4] contrib/sslinfo: Add ssl_group_info Add a new function to sslinfo ssl_group_info to show SSL groups, including negotiated, supported and shared. It's useful for diagnostic purposes, to identify what's being used and supported, e.g. which key share is being negotiated. Few examples, for openssl 3.2.4: select * from ssl_group_info(); type | name ------------+-------------------- negotiated | X25519MLKEM768 shared | X25519MLKEM768 shared | x25519 supported | X25519MLKEM768 supported | x25519 [...] The implementation is inspired by ssl_print_groups from openssl. --- contrib/sslinfo/Makefile | 2 +- contrib/sslinfo/meson.build | 1 + contrib/sslinfo/sslinfo--1.2--1.3.sql | 10 ++ contrib/sslinfo/sslinfo.c | 167 +++++++++++++++++++++++++- contrib/sslinfo/sslinfo.control | 2 +- doc/src/sgml/sslinfo.sgml | 45 +++++++ src/tools/pgindent/typedefs.list | 1 + 7 files changed, 225 insertions(+), 3 deletions(-) create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile index 14305594e2d..d968ef2abfd 100644 --- a/contrib/sslinfo/Makefile +++ b/contrib/sslinfo/Makefile @@ -6,7 +6,7 @@ OBJS = \ sslinfo.o EXTENSION = sslinfo -DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql +DATA = sslinfo--1.2.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql PGFILEDESC = "sslinfo - information about client SSL certificate" ifdef USE_PGXS diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build index 6e9cb96430a..27737562925 100644 --- a/contrib/sslinfo/meson.build +++ b/contrib/sslinfo/meson.build @@ -26,6 +26,7 @@ install_data( 'sslinfo--1.0--1.1.sql', 'sslinfo--1.1--1.2.sql', 'sslinfo--1.2.sql', + 'sslinfo--1.2--1.3.sql', 'sslinfo.control', kwargs: contrib_data_args, ) diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql new file mode 100644 index 00000000000..40fd0ea2b9c --- /dev/null +++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql @@ -0,0 +1,10 @@ +/* contrib/sslinfo/sslinfo--1.2--1.3.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit + +CREATE FUNCTION +ssl_group_info(OUT group_type text, OUT name text +) RETURNS SETOF record +AS 'MODULE_PATHNAME', 'ssl_group_info' +LANGUAGE C STRICT PARALLEL RESTRICTED; diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c index 2b9eb90b093..e018010d4be 100644 --- a/contrib/sslinfo/sslinfo.c +++ b/contrib/sslinfo/sslinfo.c @@ -28,13 +28,28 @@ static Datum X509_NAME_field_to_text(X509_NAME *name, text *fieldName); static Datum ASN1_STRING_to_text(ASN1_STRING *str); /* - * Function context for data persisting over repeated calls. + * Function context for data persisting over repeated calls of + * ssl_extension_info. */ typedef struct { TupleDesc tupdesc; } SSLExtensionInfoContext; +/* + * Function context for data persisting over repeated calls of + * ssl_group_info. + */ +typedef struct +{ + TupleDesc tupdesc; + int nshared; + int nsupported; + + /* Supported groups have to be stored separately */ + int *supported_groups; +} SSLGroupInfoContext; + /* * Indicates whether current session uses SSL * @@ -474,3 +489,153 @@ ssl_extension_info(PG_FUNCTION_ARGS) /* All done */ SRF_RETURN_DONE(funcctx); } + +/* + * Returns information about TLS groups. + * + * Returns setof record made of the following values: + * - type of the group: negotiated, shared, supported. + * - name of the group. + */ +PG_FUNCTION_INFO_V1(ssl_group_info); +Datum +ssl_group_info(PG_FUNCTION_ARGS) +{ + SSL *ssl = MyProcPort->ssl; + FuncCallContext *funcctx; + int call_cntr = 0; + int max_calls = 0; + MemoryContext oldcontext; + SSLGroupInfoContext *fctx; + + if (SRF_IS_FIRSTCALL()) + { + + TupleDesc tupdesc; + + /* create a function context for cross-call persistence */ + funcctx = SRF_FIRSTCALL_INIT(); + + /* + * Switch to memory context appropriate for multiple function calls + */ + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + /* Create a user function context for cross-call persistence */ + fctx = palloc_object(SSLGroupInfoContext); + + /* Construct tuple descriptor */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function returning record called in context that cannot accept type record"))); + fctx->tupdesc = BlessTupleDesc(tupdesc); + + if (!MyProcPort->ssl_in_use) + { + /* fast track when no results */ + MemoryContextSwitchTo(oldcontext); + SRF_RETURN_DONE(funcctx); + } + + if (ssl != NULL) + { + fctx->nsupported = SSL_get1_groups(ssl, NULL); + fctx->nshared = SSL_get_shared_group(ssl, -1); + + fctx->supported_groups = + palloc(fctx->nsupported * sizeof(*fctx->supported_groups)); + SSL_get1_groups(ssl, fctx->supported_groups); + + /* + * Set max_calls as the number of supported groups plus the number + * of shared groups plus one negotiated group. + */ + max_calls = fctx->nsupported + fctx->nshared + 1; + } + + if (max_calls > 0) + { + /* got results, keep track of them */ + funcctx->max_calls = max_calls; + funcctx->user_fctx = fctx; + } + else + { + /* fast track when no results */ + MemoryContextSwitchTo(oldcontext); + SRF_RETURN_DONE(funcctx); + } + + MemoryContextSwitchTo(oldcontext); + } + + /* stuff done on every call of the function */ + funcctx = SRF_PERCALL_SETUP(); + + /* + * Initialize per-call variables. + */ + call_cntr = funcctx->call_cntr; + max_calls = funcctx->max_calls; + fctx = funcctx->user_fctx; + + /* do while there are more left to send */ + if (call_cntr < max_calls) + { + Datum values[2]; + bool nulls[2]; + HeapTuple tuple; + Datum result, + group_type; + int nid; + const char *group_name; + + /* Send the negotiated group first */ + if (call_cntr == 0) + { + nid = SSL_get_negotiated_group(ssl); + group_type = CStringGetTextDatum("negotiated"); + } + /* Then the shared groups */ + else if (call_cntr < fctx->nshared + 1) + { + nid = SSL_get_shared_group(ssl, call_cntr - 1); + group_type = CStringGetTextDatum("shared"); + } + /* And finally the supported groups */ + else if (call_cntr < fctx->nsupported + fctx->nshared + 1) + { + nid = fctx->supported_groups[call_cntr - fctx->nshared - 1]; + group_type = CStringGetTextDatum("supported"); + } + else + SRF_RETURN_DONE(funcctx); + + /* + * SSL_group_to_name can return NULL in case of an error, e.g. when no + * such name was registered for some reason. + */ + group_name = SSL_group_to_name(ssl, nid); + if (group_name == NULL) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("unknown OpenSSL group at position %d", + call_cntr))); + + values[0] = group_type; + nulls[0] = false; + + values[1] = CStringGetTextDatum(group_name); + nulls[1] = false; + + /* Build tuple */ + tuple = heap_form_tuple(fctx->tupdesc, values, nulls); + result = HeapTupleGetDatum(tuple); + + SRF_RETURN_NEXT(funcctx, result); + } + + /* All done */ + SRF_RETURN_DONE(funcctx); +} diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control index c7754f924cf..b53e95b7da8 100644 --- a/contrib/sslinfo/sslinfo.control +++ b/contrib/sslinfo/sslinfo.control @@ -1,5 +1,5 @@ # sslinfo extension comment = 'information about SSL certificates' -default_version = '1.2' +default_version = '1.3' module_pathname = '$libdir/sslinfo' relocatable = true diff --git a/doc/src/sgml/sslinfo.sgml b/doc/src/sgml/sslinfo.sgml index 85d49f66537..422745de37c 100644 --- a/doc/src/sgml/sslinfo.sgml +++ b/doc/src/sgml/sslinfo.sgml @@ -240,6 +240,51 @@ emailAddress + + + + ssl_group_info() returns setof record + + ssl_group_info + + + + + Provide information about TLS groups: group type and group name. + The group type value could be one of the following: + + + + negotiated + + + The group used for the handshake key exchange process. + + + + + + shared + + + Lisf of named groups shared with the server side. + + + + + + supported + + + list of named groups supported by the client for key exchange in the + form of "supported_groups" extension. + + + + + + + diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 52f8603a7be..b5ea3c18291 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -2720,6 +2720,7 @@ SQLValueFunction SQLValueFunctionOp SSL SSLExtensionInfoContext +SSLGroupInfoContext SSL_CTX STARTUPINFO STRLEN base-commit: e82fc27e095b5a84c578b6e6b43b3396463bd812 -- 2.52.0 --eajje57lwcd22geu--