agora inbox for [email protected]  
help / color / mirror / Atom feed
[PATCH v14 1/7] Rejigger materializing and fetching a HeapTuple from a slot.
285+ messages / 2 participants
[nested] [flat]

* [PATCH v14 1/7] Rejigger materializing and fetching a HeapTuple from a slot.
@ 2018-10-04 09:30  Amit Khandekar <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Amit Khandekar @ 2018-10-04 09:30 UTC (permalink / raw)

Author: Ashutosh Bapat and Andres Freund
Discussion: https://postgr.es/m/[email protected]
---
 contrib/postgres_fdw/postgres_fdw.c    |  5 +-
 src/backend/commands/copy.c            |  4 +-
 src/backend/commands/createas.c        |  2 +-
 src/backend/commands/matview.c         |  5 +-
 src/backend/commands/trigger.c         | 46 ++++++++++-----
 src/backend/executor/execMain.c        |  2 +-
 src/backend/executor/execReplication.c |  4 +-
 src/backend/executor/execSRF.c         |  2 +-
 src/backend/executor/execTuples.c      | 79 +++++++++++++++-----------
 src/backend/executor/functions.c       |  2 +-
 src/backend/executor/nodeForeignscan.c |  2 +-
 src/backend/executor/nodeHash.c        | 24 ++++++--
 src/backend/executor/nodeHashjoin.c    | 17 +++++-
 src/backend/executor/nodeModifyTable.c | 26 ++++-----
 src/backend/executor/tqueue.c          | 10 +++-
 src/include/executor/tuptable.h        |  9 +--
 16 files changed, 154 insertions(+), 85 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index fd20aa96aa9..6f9c6e193fc 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -3947,11 +3947,12 @@ apply_returning_filter(PgFdwDirectModifyState *dmstate,
 	ExecStoreVirtualTuple(resultSlot);
 
 	/*
-	 * If we have any system columns to return, install them.
+	 * If we have any system columns to return, materialize a heap tuple in the
+	 * slot from column values set above and install system columns in that tuple.
 	 */
 	if (dmstate->hasSystemCols)
 	{
-		HeapTuple	resultTup = ExecMaterializeSlot(resultSlot);
+		HeapTuple	resultTup = ExecFetchSlotHeapTuple(resultSlot, true, NULL);
 
 		/* ctid */
 		if (dmstate->ctidAttno)
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index b58a74f4e3d..a9471c5ef6a 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2899,7 +2899,7 @@ CopyFrom(CopyState cstate)
 			if (slot == NULL)	/* "do nothing" */
 				skip_tuple = true;
 			else				/* trigger might have changed tuple */
-				tuple = ExecMaterializeSlot(slot);
+				tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 		}
 
 		if (!skip_tuple)
@@ -2975,7 +2975,7 @@ CopyFrom(CopyState cstate)
 							continue;	/* next tuple please */
 
 						/* FDW might have changed tuple */
-						tuple = ExecMaterializeSlot(slot);
+						tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 
 						/*
 						 * AFTER ROW Triggers might reference the tableoid
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d5cb62da15b..d8002e5b776 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -589,7 +589,7 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self)
 	 * get the heap tuple out of the tuple table slot, making sure we have a
 	 * writable copy
 	 */
-	tuple = ExecMaterializeSlot(slot);
+	tuple = ExecCopySlotTuple(slot);
 
 	/*
 	 * force assignment of new OID (see comments in ExecInsert)
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index e1eb7c374b8..9957c7074de 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -484,7 +484,7 @@ transientrel_receive(TupleTableSlot *slot, DestReceiver *self)
 	 * get the heap tuple out of the tuple table slot, making sure we have a
 	 * writable copy
 	 */
-	tuple = ExecMaterializeSlot(slot);
+	tuple = ExecCopySlotTuple(slot);
 
 	heap_insert(myState->transientrel,
 				tuple,
@@ -494,6 +494,9 @@ transientrel_receive(TupleTableSlot *slot, DestReceiver *self)
 
 	/* We know this is a newly created relation, so there are no indexes */
 
+	/* Free the copied tuple. */
+	heap_freetuple(tuple);
+
 	return true;
 }
 
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index ccb5706c162..d6f33ecbd04 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2517,7 +2517,8 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
 					 TupleTableSlot *slot)
 {
 	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
-	HeapTuple	slottuple = ExecMaterializeSlot(slot);
+	bool		should_free;
+	HeapTuple	slottuple = ExecFetchSlotHeapTuple(slot, true, &should_free);
 	HeapTuple	newtuple = slottuple;
 	HeapTuple	oldtuple;
 	TriggerData LocTriggerData;
@@ -2556,7 +2557,11 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
 		if (oldtuple != newtuple && oldtuple != slottuple)
 			heap_freetuple(oldtuple);
 		if (newtuple == NULL)
+		{
+			if (should_free)
+				heap_freetuple(slottuple);
 			return NULL;		/* "do nothing" */
+		}
 	}
 
 	if (newtuple != slottuple)
@@ -2575,6 +2580,9 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
 		ExecStoreHeapTuple(newtuple, newslot, false);
 		slot = newslot;
 	}
+
+	if (should_free)
+		heap_freetuple(slottuple);
 	return slot;
 }
 
@@ -2598,7 +2606,8 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
 					 TupleTableSlot *slot)
 {
 	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
-	HeapTuple	slottuple = ExecMaterializeSlot(slot);
+	bool		should_free;
+	HeapTuple	slottuple = ExecFetchSlotHeapTuple(slot, true, &should_free);
 	HeapTuple	newtuple = slottuple;
 	HeapTuple	oldtuple;
 	TriggerData LocTriggerData;
@@ -2637,7 +2646,11 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
 		if (oldtuple != newtuple && oldtuple != slottuple)
 			heap_freetuple(oldtuple);
 		if (newtuple == NULL)
+		{
+			if (should_free)
+				heap_freetuple(slottuple);
 			return NULL;		/* "do nothing" */
+		}
 	}
 
 	if (newtuple != slottuple)
@@ -2656,6 +2669,9 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
 		ExecStoreHeapTuple(newtuple, newslot, false);
 		slot = newslot;
 	}
+
+	if (should_free)
+		heap_freetuple(slottuple);
 	return slot;
 }
 
@@ -2976,7 +2992,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
 					 TupleTableSlot *slot)
 {
 	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
-	HeapTuple	slottuple = ExecMaterializeSlot(slot);
+	HeapTuple	slottuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 	HeapTuple	newtuple = slottuple;
 	TriggerData LocTriggerData;
 	HeapTuple	trigtuple;
@@ -3018,7 +3034,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
 	if (newSlot != NULL)
 	{
 		slot = ExecFilterJunk(relinfo->ri_junkFilter, newSlot);
-		slottuple = ExecMaterializeSlot(slot);
+		slottuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 		newtuple = slottuple;
 	}
 
@@ -3132,7 +3148,7 @@ ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
 					 HeapTuple trigtuple, TupleTableSlot *slot)
 {
 	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
-	HeapTuple	slottuple = ExecMaterializeSlot(slot);
+	HeapTuple	slottuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 	HeapTuple	newtuple = slottuple;
 	TriggerData LocTriggerData;
 	HeapTuple	oldtuple;
@@ -4262,22 +4278,22 @@ AfterTriggerExecute(AfterTriggerEvent event,
 		case AFTER_TRIGGER_FDW_REUSE:
 
 			/*
-			 * Using ExecMaterializeSlot() rather than ExecFetchSlotTuple()
-			 * ensures that tg_trigtuple does not reference tuplestore memory.
-			 * (It is formally possible for the trigger function to queue
-			 * trigger events that add to the same tuplestore, which can push
-			 * other tuples out of memory.)  The distinction is academic,
-			 * because we start with a minimal tuple that ExecFetchSlotTuple()
-			 * must materialize anyway.
+			 * Materialize tuple in the slot so that tg_trigtuple does not
+			 * reference tuplestore memory.  (It is formally possible for the
+			 * trigger function to queue trigger events that add to the same
+			 * tuplestore, which can push other tuples out of memory.)  The
+			 * distinction is academic, because we start with a minimal tuple
+			 * that is stored as a heap tuple, constructed in different memory
+			 * context, in the slot anyway.
 			 */
-			LocTriggerData.tg_trigtuple =
-				ExecMaterializeSlot(trig_tuple_slot1);
+			LocTriggerData.tg_trigtuple = ExecFetchSlotHeapTuple(trig_tuple_slot1,
+																	true, NULL);
 			LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
 
 			LocTriggerData.tg_newtuple =
 				((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
 				 TRIGGER_EVENT_UPDATE) ?
-				ExecMaterializeSlot(trig_tuple_slot2) : NULL;
+				ExecFetchSlotHeapTuple(trig_tuple_slot2, true, NULL) : NULL;
 			LocTriggerData.tg_newtuplebuf = InvalidBuffer;
 
 			break;
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index ba156f8c5fc..d10e533fd16 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -2549,7 +2549,7 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
 	 * is to guard against early re-use of the EPQ query.
 	 */
 	if (!TupIsNull(slot))
-		(void) ExecMaterializeSlot(slot);
+		ExecMaterializeSlot(slot);
 
 	/*
 	 * Clear out the test tuple.  This is needed in case the EPQ query is
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index 25ba93e03c3..071ba8762d4 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -418,7 +418,7 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
 			ExecPartitionCheck(resultRelInfo, slot, estate, true);
 
 		/* Materialize slot into a tuple that we can scribble upon. */
-		tuple = ExecMaterializeSlot(slot);
+		tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 
 		/* OK, store the tuple and create index entries for it */
 		simple_heap_insert(rel, tuple);
@@ -485,7 +485,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
 			ExecPartitionCheck(resultRelInfo, slot, estate, true);
 
 		/* Materialize slot into a tuple that we can scribble upon. */
-		tuple = ExecMaterializeSlot(slot);
+		tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 
 		/* OK, update the tuple and index entries for it */
 		simple_heap_update(rel, &searchslot->tts_tuple->t_self,
diff --git a/src/backend/executor/execSRF.c b/src/backend/executor/execSRF.c
index b97b8d797ec..3bffb0ea71f 100644
--- a/src/backend/executor/execSRF.c
+++ b/src/backend/executor/execSRF.c
@@ -521,7 +521,7 @@ restart:
 			{
 				/* We must return the whole tuple as a Datum. */
 				*isNull = false;
-				return ExecFetchSlotTupleDatum(fcache->funcResultSlot);
+				return ExecFetchSlotHeapTupleDatum(fcache->funcResultSlot);
 			}
 			else
 			{
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 9f0d9daa829..ca2ca7f2d63 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -676,23 +676,27 @@ ExecCopySlotMinimalTuple(TupleTableSlot *slot)
 								   slot->tts_isnull);
 }
 
-/* --------------------------------
- *		ExecFetchSlotTuple
- *			Fetch the slot's regular physical tuple.
+/*
+ * ExecFetchSlotHeapTuple - fetch HeapTuple representing the slot's content
  *
- *		If the slot contains a virtual tuple, we convert it to physical
- *		form.  The slot retains ownership of the physical tuple.
- *		If it contains a minimal tuple we convert to regular form and store
- *		that in addition to the minimal tuple (not instead of, because
- *		callers may hold pointers to Datums within the minimal tuple).
+ * The returned HeapTuple represents the slot's content as closely as
+ * possible.
  *
- * The main difference between this and ExecMaterializeSlot() is that this
- * does not guarantee that the contained tuple is local storage.
- * Hence, the result must be treated as read-only.
- * --------------------------------
+ * If materialize is true, the contents of the slots will be made independent
+ * from the underlying storage (i.e. all buffer pins are release, memory is
+ * allocated in the slot's context).
+ *
+ * If shouldFree is not-NULL it'll be set to true if the returned tuple has
+ * been allocated in the calling memory context, and must be freed by the
+ * caller (via explicit pfree() or a memory context reset).
+ *
+ * NB: If materialize is true, modifications of the returned tuple are
+ * allowed. But it depends on the type of the slot whether such modifications
+ * will also affect the slot's contents. While that is not the nicest
+ * behaviour, all such modifcations are in the process of being removed.
  */
 HeapTuple
-ExecFetchSlotTuple(TupleTableSlot *slot)
+ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
 {
 	/*
 	 * sanity checks
@@ -700,6 +704,9 @@ ExecFetchSlotTuple(TupleTableSlot *slot)
 	Assert(slot != NULL);
 	Assert(!TTS_EMPTY(slot));
 
+	if (shouldFree)
+		*shouldFree = false;
+
 	/*
 	 * If we have a regular physical tuple then just return it.
 	 */
@@ -722,7 +729,9 @@ ExecFetchSlotTuple(TupleTableSlot *slot)
 	/*
 	 * Otherwise materialize the slot...
 	 */
-	return ExecMaterializeSlot(slot);
+	ExecMaterializeSlot(slot);
+
+	return slot->tts_tuple;
 }
 
 /* --------------------------------
@@ -739,7 +748,7 @@ ExecFetchSlotTuple(TupleTableSlot *slot)
  * --------------------------------
  */
 MinimalTuple
-ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
+ExecFetchSlotMinimalTuple(TupleTableSlot *slot, bool *shouldFree)
 {
 	MemoryContext oldContext;
 
@@ -749,6 +758,8 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
 	Assert(slot != NULL);
 	Assert(!TTS_EMPTY(slot));
 
+	if (shouldFree)
+		*shouldFree = false;
 
 	/*
 	 * If we have a minimal physical tuple (local or not) then just return it.
@@ -779,40 +790,44 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
 }
 
 /* --------------------------------
- *		ExecFetchSlotTupleDatum
+ *		ExecFetchSlotHeapTupleDatum
  *			Fetch the slot's tuple as a composite-type Datum.
  *
  *		The result is always freshly palloc'd in the caller's memory context.
  * --------------------------------
  */
 Datum
-ExecFetchSlotTupleDatum(TupleTableSlot *slot)
+ExecFetchSlotHeapTupleDatum(TupleTableSlot *slot)
 {
 	HeapTuple	tup;
 	TupleDesc	tupdesc;
+	bool		shouldFree;
+	Datum		ret;
 
 	/* Fetch slot's contents in regular-physical-tuple form */
-	tup = ExecFetchSlotTuple(slot);
+	tup = ExecFetchSlotHeapTuple(slot, false, &shouldFree);
 	tupdesc = slot->tts_tupleDescriptor;
 
 	/* Convert to Datum form */
-	return heap_copy_tuple_as_datum(tup, tupdesc);
+	ret = heap_copy_tuple_as_datum(tup, tupdesc);
+
+	if (shouldFree)
+		pfree(tup);
+
+	return ret;
 }
 
-/* --------------------------------
- *		ExecMaterializeSlot
- *			Force a slot into the "materialized" state.
+/* ExecMaterializeSlot - force a slot into the "materialized" state.
  *
- *		This causes the slot's tuple to be a local copy not dependent on
- *		any external storage.  A pointer to the contained tuple is returned.
+ * This causes the slot's tuple to be a local copy not dependent on any
+ * external storage (i.e. pointing into a Buffer, or having allocations in
+ * another memory context).
  *
- *		A typical use for this operation is to prepare a computed tuple
- *		for being stored on disk.  The original data may or may not be
- *		virtual, but in any case we need a private copy for heap_insert
- *		to scribble on.
- * --------------------------------
+ * A typical use for this operation is to prepare a computed tuple for being
+ * stored on disk.  The original data may or may not be virtual, but in any
+ * case we need a private copy for heap_insert to scribble on.
  */
-HeapTuple
+void
 ExecMaterializeSlot(TupleTableSlot *slot)
 {
 	MemoryContext oldContext;
@@ -828,7 +843,7 @@ ExecMaterializeSlot(TupleTableSlot *slot)
 	 * nothing to do.
 	 */
 	if (slot->tts_tuple && TTS_SHOULDFREE(slot))
-		return slot->tts_tuple;
+		return;
 
 	/*
 	 * Otherwise, copy or build a physical tuple, and store it into the slot.
@@ -868,8 +883,6 @@ ExecMaterializeSlot(TupleTableSlot *slot)
 	 */
 	if (!TTS_SHOULDFREEMIN(slot))
 		slot->tts_mintuple = NULL;
-
-	return slot->tts_tuple;
 }
 
 /* --------------------------------
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 23545896d4d..f4dd5732198 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -969,7 +969,7 @@ postquel_get_single_result(TupleTableSlot *slot,
 	{
 		/* We must return the whole tuple as a Datum. */
 		fcinfo->isnull = false;
-		value = ExecFetchSlotTupleDatum(slot);
+		value = ExecFetchSlotHeapTupleDatum(slot);
 	}
 	else
 	{
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 5d2cd0ed717..f7eef32f6fe 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -62,7 +62,7 @@ ForeignNext(ForeignScanState *node)
 	 */
 	if (plan->fsSystemCol && !TupIsNull(slot))
 	{
-		HeapTuple	tup = ExecMaterializeSlot(slot);
+		HeapTuple	tup = ExecFetchSlotHeapTuple(slot, true, NULL);
 
 		tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
 	}
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index a9f812d66b8..5a9f1ea3c55 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -1590,7 +1590,8 @@ ExecHashTableInsert(HashJoinTable hashtable,
 					TupleTableSlot *slot,
 					uint32 hashvalue)
 {
-	MinimalTuple tuple = ExecFetchSlotMinimalTuple(slot);
+	bool		shouldFree;
+	MinimalTuple tuple = ExecFetchSlotMinimalTuple(slot, &shouldFree);
 	int			bucketno;
 	int			batchno;
 
@@ -1664,6 +1665,9 @@ ExecHashTableInsert(HashJoinTable hashtable,
 							  hashvalue,
 							  &hashtable->innerBatchFile[batchno]);
 	}
+
+	if (shouldFree)
+		heap_free_minimal_tuple(tuple);
 }
 
 /*
@@ -1675,7 +1679,8 @@ ExecParallelHashTableInsert(HashJoinTable hashtable,
 							TupleTableSlot *slot,
 							uint32 hashvalue)
 {
-	MinimalTuple tuple = ExecFetchSlotMinimalTuple(slot);
+	bool		shouldFree;
+	MinimalTuple tuple = ExecFetchSlotMinimalTuple(slot, &shouldFree);
 	dsa_pointer shared;
 	int			bucketno;
 	int			batchno;
@@ -1723,6 +1728,9 @@ retry:
 					 tuple);
 	}
 	++hashtable->batches[batchno].ntuples;
+
+	if (shouldFree)
+		heap_free_minimal_tuple(tuple);
 }
 
 /*
@@ -1736,7 +1744,8 @@ ExecParallelHashTableInsertCurrentBatch(HashJoinTable hashtable,
 										TupleTableSlot *slot,
 										uint32 hashvalue)
 {
-	MinimalTuple tuple = ExecFetchSlotMinimalTuple(slot);
+	bool		shouldFree;
+	MinimalTuple tuple = ExecFetchSlotMinimalTuple(slot, &shouldFree);
 	HashJoinTuple hashTuple;
 	dsa_pointer shared;
 	int			batchno;
@@ -1752,6 +1761,9 @@ ExecParallelHashTableInsertCurrentBatch(HashJoinTable hashtable,
 	HeapTupleHeaderClearMatch(HJTUPLE_MINTUPLE(hashTuple));
 	ExecParallelHashPushTuple(&hashtable->buckets.shared[bucketno],
 							  hashTuple, shared);
+
+	if (shouldFree)
+		heap_free_minimal_tuple(tuple);
 }
 
 /*
@@ -2391,7 +2403,8 @@ ExecHashSkewTableInsert(HashJoinTable hashtable,
 						uint32 hashvalue,
 						int bucketNumber)
 {
-	MinimalTuple tuple = ExecFetchSlotMinimalTuple(slot);
+	bool		shouldFree;
+	MinimalTuple tuple = ExecFetchSlotMinimalTuple(slot, &shouldFree);
 	HashJoinTuple hashTuple;
 	int			hashTupleSize;
 
@@ -2419,6 +2432,9 @@ ExecHashSkewTableInsert(HashJoinTable hashtable,
 	/* Check we are not over the total spaceAllowed, either */
 	if (hashtable->spaceUsed > hashtable->spaceAllowed)
 		ExecHashIncreaseNumBatches(hashtable);
+
+	if (shouldFree)
+		heap_free_minimal_tuple(tuple);
 }
 
 /*
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 08a8bb3426c..d6a6ef770dd 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -389,16 +389,22 @@ ExecHashJoinImpl(PlanState *pstate, bool parallel)
 				if (batchno != hashtable->curbatch &&
 					node->hj_CurSkewBucketNo == INVALID_SKEW_BUCKET_NO)
 				{
+					bool		shouldFree;
+					MinimalTuple mintuple = ExecFetchSlotMinimalTuple(outerTupleSlot,
+																	  &shouldFree);
+
 					/*
 					 * Need to postpone this outer tuple to a later batch.
 					 * Save it in the corresponding outer-batch file.
 					 */
 					Assert(parallel_state == NULL);
 					Assert(batchno > hashtable->curbatch);
-					ExecHashJoinSaveTuple(ExecFetchSlotMinimalTuple(outerTupleSlot),
-										  hashvalue,
+					ExecHashJoinSaveTuple(mintuple, hashvalue,
 										  &hashtable->outerBatchFile[batchno]);
 
+					if (shouldFree)
+						heap_free_minimal_tuple(mintuple);
+
 					/* Loop around, staying in HJ_NEED_NEW_OUTER state */
 					continue;
 				}
@@ -1404,11 +1410,16 @@ ExecParallelHashJoinPartitionOuter(HashJoinState *hjstate)
 		{
 			int			batchno;
 			int			bucketno;
+			bool		shouldFree;
+			MinimalTuple mintup = ExecFetchSlotMinimalTuple(slot, &shouldFree);
 
 			ExecHashGetBucketAndBatch(hashtable, hashvalue, &bucketno,
 									  &batchno);
 			sts_puttuple(hashtable->batches[batchno].outer_tuples,
-						 &hashvalue, ExecFetchSlotMinimalTuple(slot));
+						 &hashvalue, mintup);
+
+			if (shouldFree)
+				heap_free_minimal_tuple(mintup);
 		}
 		CHECK_FOR_INTERRUPTS();
 	}
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index e2836b75ff3..ca7ece3235f 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -181,7 +181,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
 		 * initialize t_tableOid before evaluating them.
 		 */
 		Assert(!TupIsNull(econtext->ecxt_scantuple));
-		tuple = ExecMaterializeSlot(econtext->ecxt_scantuple);
+		tuple = ExecFetchSlotHeapTuple(econtext->ecxt_scantuple, true, NULL);
 		tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
 	}
 	econtext->ecxt_outertuple = planSlot;
@@ -280,7 +280,7 @@ ExecInsert(ModifyTableState *mtstate,
 	 * get the heap tuple out of the tuple table slot, making sure we have a
 	 * writable copy
 	 */
-	tuple = ExecMaterializeSlot(slot);
+	tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 
 	/*
 	 * get information on the (current) result relation
@@ -321,7 +321,7 @@ ExecInsert(ModifyTableState *mtstate,
 			return NULL;
 
 		/* trigger might have changed tuple */
-		tuple = ExecMaterializeSlot(slot);
+		tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 	}
 
 	/* INSTEAD OF ROW INSERT Triggers */
@@ -334,7 +334,7 @@ ExecInsert(ModifyTableState *mtstate,
 			return NULL;
 
 		/* trigger might have changed tuple */
-		tuple = ExecMaterializeSlot(slot);
+		tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 
 		newId = InvalidOid;
 	}
@@ -352,7 +352,7 @@ ExecInsert(ModifyTableState *mtstate,
 			return NULL;
 
 		/* FDW might have changed tuple */
-		tuple = ExecMaterializeSlot(slot);
+		tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 
 		/*
 		 * AFTER ROW Triggers or RETURNING expressions might reference the
@@ -701,7 +701,7 @@ ExecDelete(ModifyTableState *mtstate,
 		 */
 		if (TTS_EMPTY(slot))
 			ExecStoreAllNullTuple(slot);
-		tuple = ExecMaterializeSlot(slot);
+		tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 		tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
 	}
 	else
@@ -959,7 +959,7 @@ ExecUpdate(ModifyTableState *mtstate,
 	 * get the heap tuple out of the tuple table slot, making sure we have a
 	 * writable copy
 	 */
-	tuple = ExecMaterializeSlot(slot);
+	tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 
 	/*
 	 * get information on the (current) result relation
@@ -978,7 +978,7 @@ ExecUpdate(ModifyTableState *mtstate,
 			return NULL;
 
 		/* trigger might have changed tuple */
-		tuple = ExecMaterializeSlot(slot);
+		tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 	}
 
 	/* INSTEAD OF ROW UPDATE Triggers */
@@ -992,7 +992,7 @@ ExecUpdate(ModifyTableState *mtstate,
 			return NULL;
 
 		/* trigger might have changed tuple */
-		tuple = ExecMaterializeSlot(slot);
+		tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 	}
 	else if (resultRelInfo->ri_FdwRoutine)
 	{
@@ -1008,7 +1008,7 @@ ExecUpdate(ModifyTableState *mtstate,
 			return NULL;
 
 		/* FDW might have changed tuple */
-		tuple = ExecMaterializeSlot(slot);
+		tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 
 		/*
 		 * AFTER ROW Triggers or RETURNING expressions might reference the
@@ -1135,7 +1135,7 @@ lreplace:;
 				else
 				{
 					slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
-					tuple = ExecMaterializeSlot(slot);
+					tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 					goto lreplace;
 				}
 			}
@@ -1274,7 +1274,7 @@ lreplace:;
 					{
 						*tupleid = hufd.ctid;
 						slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
-						tuple = ExecMaterializeSlot(slot);
+						tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 						goto lreplace;
 					}
 				}
@@ -1751,7 +1751,7 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate,
 	estate->es_result_relation_info = partrel;
 
 	/* Get the heap tuple out of the given slot. */
-	tuple = ExecMaterializeSlot(slot);
+	tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 
 	/*
 	 * If we're capturing transition tuples, we might need to convert from the
diff --git a/src/backend/executor/tqueue.c b/src/backend/executor/tqueue.c
index ecdbe7f79f6..efce1bedf58 100644
--- a/src/backend/executor/tqueue.c
+++ b/src/backend/executor/tqueue.c
@@ -56,11 +56,19 @@ tqueueReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
 	TQueueDestReceiver *tqueue = (TQueueDestReceiver *) self;
 	HeapTuple	tuple;
 	shm_mq_result result;
+	bool		should_free;
+
+	/* Get the tuple out of slot, if necessary converting the slot's contents
+	 * into a heap tuple by copying. In the later case we need to free the copy.
+	 */
+	tuple = ExecFetchSlotHeapTuple(slot, true, &should_free);
 
 	/* Send the tuple itself. */
-	tuple = ExecMaterializeSlot(slot);
 	result = shm_mq_send(tqueue->queue, tuple->t_len, tuple->t_data, false);
 
+	if (should_free)
+		heap_freetuple(tuple);
+
 	/* Check for failure. */
 	if (result == SHM_MQ_DETACHED)
 		return false;
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index b41b400ef18..8bfa73c30ea 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -185,10 +185,11 @@ extern TupleTableSlot *ExecStoreVirtualTuple(TupleTableSlot *slot);
 extern TupleTableSlot *ExecStoreAllNullTuple(TupleTableSlot *slot);
 extern HeapTuple ExecCopySlotTuple(TupleTableSlot *slot);
 extern MinimalTuple ExecCopySlotMinimalTuple(TupleTableSlot *slot);
-extern HeapTuple ExecFetchSlotTuple(TupleTableSlot *slot);
-extern MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot *slot);
-extern Datum ExecFetchSlotTupleDatum(TupleTableSlot *slot);
-extern HeapTuple ExecMaterializeSlot(TupleTableSlot *slot);
+extern HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shoulFree);
+extern MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot *slot,
+						  bool *shouldFree);
+extern Datum ExecFetchSlotHeapTupleDatum(TupleTableSlot *slot);
+extern void ExecMaterializeSlot(TupleTableSlot *slot);
 extern TupleTableSlot *ExecCopySlot(TupleTableSlot *dstslot,
 			 TupleTableSlot *srcslot);
 extern void slot_getmissingattrs(TupleTableSlot *slot, int startAttNum,
-- 
2.18.0.rc2.dirty


--ll5lqoiis2grgqzk
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="v14-0002-Change-tuple-table-slot-creation-routines-to-sui.patch"



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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

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

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

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

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

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

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

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

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

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

And the same for openssl 3.5 with different defaults:

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

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

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33  Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.3.sql sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
+  'sslinfo--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





^ permalink  raw  reply  [nested|flat] 285+ messages in thread


end of thread, other threads:[~2026-02-19 15:33 UTC | newest]

Thread overview: 285+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2018-10-04 09:30 [PATCH v14 1/7] Rejigger materializing and fetching a HeapTuple from a slot. Amit Khandekar <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|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