Skip to content

Commit 4912461

Browse files
committed
contrib/sslinfo: add ssl_extension_info SRF
This new function provides information about SSL extensions present in the X509 certificate used for the current connection. Extension version updated to version 1.1. Author: Дмитрий Воронин (Dmitry Voronin) Reviewed by: Michael Paquier, Heikki Linnakangas, Álvaro Herrera
1 parent 582fbff commit 4912461

File tree

6 files changed

+202
-9
lines changed

6 files changed

+202
-9
lines changed

contrib/sslinfo/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ MODULE_big = sslinfo
44
OBJS = sslinfo.o $(WIN32RES)
55

66
EXTENSION = sslinfo
7-
DATA = sslinfo--1.0.sql sslinfo--unpackaged--1.0.sql
7+
DATA = sslinfo--1.0--1.1.sql sslinfo--1.1.sql \
8+
sslinfo--unpackaged--1.0.sql
89
PGFILEDESC = "sslinfo - information about client SSL certificate"
910

1011
ifdef USE_PGXS

contrib/sslinfo/sslinfo--1.0--1.1.sql

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/* contrib/sslinfo/sslinfo--1.0--1.1.sql */
2+
3+
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
4+
\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.1'" to load this file. \quit
5+
6+
CREATE OR REPLACE FUNCTION
7+
ssl_extension_info(OUT name text,
8+
OUT value text,
9+
OUT critical boolean
10+
) RETURNS SETOF record
11+
AS 'MODULE_PATHNAME', 'ssl_extension_info'
12+
LANGUAGE C STRICT;

contrib/sslinfo/sslinfo--1.0.sql renamed to contrib/sslinfo/sslinfo--1.1.sql

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* contrib/sslinfo/sslinfo--1.0.sql */
1+
/* contrib/sslinfo/sslinfo--1.1.sql */
22

33
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
44
\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
@@ -38,3 +38,11 @@ LANGUAGE C STRICT;
3838
CREATE FUNCTION ssl_issuer_dn() RETURNS text
3939
AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
4040
LANGUAGE C STRICT;
41+
42+
CREATE FUNCTION
43+
ssl_extension_info(OUT name text,
44+
OUT value text,
45+
OUT critical boolean
46+
) RETURNS SETOF record
47+
AS 'MODULE_PATHNAME', 'ssl_extension_info'
48+
LANGUAGE C STRICT;

contrib/sslinfo/sslinfo.c

Lines changed: 159 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,30 @@
88
*/
99

1010
#include "postgres.h"
11-
#include "fmgr.h"
12-
#include "utils/numeric.h"
13-
#include "libpq/libpq-be.h"
14-
#include "miscadmin.h"
15-
#include "utils/builtins.h"
16-
#include "mb/pg_wchar.h"
1711

1812
#include <openssl/x509.h>
13+
#include <openssl/x509v3.h>
1914
#include <openssl/asn1.h>
2015

16+
#include "access/htup_details.h"
17+
#include "funcapi.h"
18+
#include "libpq/libpq-be.h"
19+
#include "miscadmin.h"
20+
#include "utils/builtins.h"
21+
2122
PG_MODULE_MAGIC;
2223

2324
static Datum X509_NAME_field_to_text(X509_NAME *name, text *fieldName);
2425
static Datum X509_NAME_to_text(X509_NAME *name);
2526
static Datum ASN1_STRING_to_text(ASN1_STRING *str);
2627

28+
/*
29+
* Function context for data persisting over repeated calls.
30+
*/
31+
typedef struct
32+
{
33+
TupleDesc tupdesc;
34+
} SSLExtensionInfoContext;
2735

2836
/*
2937
* Indicates whether current session uses SSL
@@ -373,3 +381,148 @@ ssl_issuer_dn(PG_FUNCTION_ARGS)
373381
PG_RETURN_NULL();
374382
return X509_NAME_to_text(X509_get_issuer_name(MyProcPort->peer));
375383
}
384+
385+
386+
/*
387+
* Returns information about available SSL extensions.
388+
*
389+
* Returns setof record made of the following values:
390+
* - name of the extension.
391+
* - value of the extension.
392+
* - critical status of the extension.
393+
*/
394+
PG_FUNCTION_INFO_V1(ssl_extension_info);
395+
Datum
396+
ssl_extension_info(PG_FUNCTION_ARGS)
397+
{
398+
X509 *cert = MyProcPort->peer;
399+
FuncCallContext *funcctx;
400+
int call_cntr;
401+
int max_calls;
402+
MemoryContext oldcontext;
403+
SSLExtensionInfoContext *fctx;
404+
405+
STACK_OF(X509_EXTENSION) *ext_stack = NULL;
406+
407+
if (SRF_IS_FIRSTCALL())
408+
{
409+
410+
TupleDesc tupdesc;
411+
412+
/* create a function context for cross-call persistence */
413+
funcctx = SRF_FIRSTCALL_INIT();
414+
415+
/*
416+
* Switch to memory context appropriate for multiple function calls
417+
*/
418+
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
419+
420+
/* Create a user function context for cross-call persistence */
421+
fctx = (SSLExtensionInfoContext *) palloc(sizeof(SSLExtensionInfoContext));
422+
423+
/* Construct tuple descriptor */
424+
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
425+
ereport(ERROR,
426+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
427+
errmsg("function returning record called in context that cannot accept type record")));
428+
fctx->tupdesc = BlessTupleDesc(tupdesc);
429+
430+
/* Get all extensions of certificate */
431+
if (cert && cert->cert_info)
432+
ext_stack = cert->cert_info->extensions;
433+
434+
/* Set max_calls as a count of extensions in certificate */
435+
max_calls = cert != NULL ? X509_get_ext_count(cert) : 0;
436+
437+
if (cert != NULL &&
438+
ext_stack != NULL &&
439+
max_calls > 0)
440+
{
441+
/* got results, keep track of them */
442+
funcctx->max_calls = max_calls;
443+
funcctx->user_fctx = fctx;
444+
}
445+
else
446+
{
447+
/* fast track when no results */
448+
MemoryContextSwitchTo(oldcontext);
449+
SRF_RETURN_DONE(funcctx);
450+
}
451+
452+
MemoryContextSwitchTo(oldcontext);
453+
}
454+
455+
/* stuff done on every call of the function */
456+
funcctx = SRF_PERCALL_SETUP();
457+
458+
/*
459+
* Initialize per-call variables.
460+
*/
461+
call_cntr = funcctx->call_cntr;
462+
max_calls = funcctx->max_calls;
463+
fctx = funcctx->user_fctx;
464+
465+
ext_stack = cert->cert_info->extensions;
466+
467+
/* do while there are more left to send */
468+
if (call_cntr < max_calls)
469+
{
470+
Datum values[3];
471+
bool nulls[3];
472+
char *buf;
473+
HeapTuple tuple;
474+
Datum result;
475+
BIO *membuf;
476+
X509_EXTENSION *ext;
477+
ASN1_OBJECT *obj;
478+
int nid;
479+
int len;
480+
481+
/* need a BIO for this */
482+
membuf = BIO_new(BIO_s_mem());
483+
if (membuf == NULL)
484+
ereport(ERROR,
485+
(errcode(ERRCODE_OUT_OF_MEMORY),
486+
errmsg("could not create OpenSSL BIO structure")));
487+
488+
/* Get the extension from the certificate */
489+
ext = sk_X509_EXTENSION_value(ext_stack, call_cntr);
490+
obj = X509_EXTENSION_get_object(ext);
491+
492+
/* Get the extension name */
493+
nid = OBJ_obj2nid(obj);
494+
if (nid == NID_undef)
495+
ereport(ERROR,
496+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
497+
errmsg("unknown OpenSSL extension in certificate at position %d",
498+
call_cntr)));
499+
values[0] = CStringGetTextDatum(OBJ_nid2sn(nid));
500+
nulls[0] = false;
501+
502+
/* Get the extension value */
503+
if (X509V3_EXT_print(membuf, ext, 0, 0) <= 0)
504+
ereport(ERROR,
505+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
506+
errmsg("could not print extension value in certificate at position %d",
507+
call_cntr)));
508+
len = BIO_get_mem_data(membuf, &buf);
509+
values[1] = PointerGetDatum(cstring_to_text_with_len(buf, len));
510+
nulls[1] = false;
511+
512+
/* Get critical status */
513+
values[2] = BoolGetDatum(X509_EXTENSION_get_critical(ext));
514+
nulls[2] = false;
515+
516+
/* Build tuple */
517+
tuple = heap_form_tuple(fctx->tupdesc, values, nulls);
518+
result = HeapTupleGetDatum(tuple);
519+
520+
if (BIO_free(membuf) != 1)
521+
elog(ERROR, "could not free OpenSSL BIO structure");
522+
523+
SRF_RETURN_NEXT(funcctx, result);
524+
}
525+
526+
/* Do when there is no more left */
527+
SRF_RETURN_DONE(funcctx);
528+
}

contrib/sslinfo/sslinfo.control

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# sslinfo extension
22
comment = 'information about SSL certificates'
3-
default_version = '1.0'
3+
default_version = '1.1'
44
module_pathname = '$libdir/sslinfo'
55
relocatable = true

doc/src/sgml/sslinfo.sgml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,21 @@ emailAddress
219219
</para>
220220
</listitem>
221221
</varlistentry>
222+
223+
<varlistentry>
224+
<term>
225+
<function>ssl_extension_info() returns setof record</function>
226+
<indexterm>
227+
<primary>ssl_extension_info</primary>
228+
</indexterm>
229+
</term>
230+
<listitem>
231+
<para>
232+
Provide information about extensions of client certificate: extension name,
233+
extension value, and if it is a critical extension.
234+
</para>
235+
</listitem>
236+
</varlistentry>
222237
</variablelist>
223238
</sect2>
224239

@@ -229,6 +244,10 @@ emailAddress
229244
Victor Wagner <email>vitus@cryptocom.ru</email>, Cryptocom LTD
230245
</para>
231246

247+
<para>
248+
Dmitry Voronin <email>carriingfate92@yandex.ru</email>
249+
</para>
250+
232251
<para>
233252
E-Mail of Cryptocom OpenSSL development group:
234253
<email>openssl@cryptocom.ru</email>

0 commit comments

Comments
 (0)