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
This commit is contained in:
Alvaro Herrera 2015-09-07 21:24:17 -03:00
parent 582fbffb0c
commit 49124613f1
6 changed files with 202 additions and 9 deletions

View File

@ -4,7 +4,8 @@ MODULE_big = sslinfo
OBJS = sslinfo.o $(WIN32RES) OBJS = sslinfo.o $(WIN32RES)
EXTENSION = sslinfo EXTENSION = sslinfo
DATA = sslinfo--1.0.sql sslinfo--unpackaged--1.0.sql DATA = sslinfo--1.0--1.1.sql sslinfo--1.1.sql \
sslinfo--unpackaged--1.0.sql
PGFILEDESC = "sslinfo - information about client SSL certificate" PGFILEDESC = "sslinfo - information about client SSL certificate"
ifdef USE_PGXS ifdef USE_PGXS

View File

@ -0,0 +1,12 @@
/* contrib/sslinfo/sslinfo--1.0--1.1.sql */
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.1'" to load this file. \quit
CREATE OR REPLACE FUNCTION
ssl_extension_info(OUT name text,
OUT value text,
OUT critical boolean
) RETURNS SETOF record
AS 'MODULE_PATHNAME', 'ssl_extension_info'
LANGUAGE C STRICT;

View File

@ -1,4 +1,4 @@
/* contrib/sslinfo/sslinfo--1.0.sql */ /* contrib/sslinfo/sslinfo--1.1.sql */
-- complain if script is sourced in psql, rather than via CREATE EXTENSION -- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit \echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
@ -38,3 +38,11 @@ LANGUAGE C STRICT;
CREATE FUNCTION ssl_issuer_dn() RETURNS text CREATE FUNCTION ssl_issuer_dn() RETURNS text
AS 'MODULE_PATHNAME', 'ssl_issuer_dn' AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
LANGUAGE C STRICT; LANGUAGE C STRICT;
CREATE FUNCTION
ssl_extension_info(OUT name text,
OUT value text,
OUT critical boolean
) RETURNS SETOF record
AS 'MODULE_PATHNAME', 'ssl_extension_info'
LANGUAGE C STRICT;

View File

@ -8,15 +8,16 @@
*/ */
#include "postgres.h" #include "postgres.h"
#include "fmgr.h"
#include "utils/numeric.h" #include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/asn1.h>
#include "access/htup_details.h"
#include "funcapi.h"
#include "libpq/libpq-be.h" #include "libpq/libpq-be.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "mb/pg_wchar.h"
#include <openssl/x509.h>
#include <openssl/asn1.h>
PG_MODULE_MAGIC; PG_MODULE_MAGIC;
@ -24,6 +25,13 @@ static Datum X509_NAME_field_to_text(X509_NAME *name, text *fieldName);
static Datum X509_NAME_to_text(X509_NAME *name); static Datum X509_NAME_to_text(X509_NAME *name);
static Datum ASN1_STRING_to_text(ASN1_STRING *str); static Datum ASN1_STRING_to_text(ASN1_STRING *str);
/*
* Function context for data persisting over repeated calls.
*/
typedef struct
{
TupleDesc tupdesc;
} SSLExtensionInfoContext;
/* /*
* Indicates whether current session uses SSL * Indicates whether current session uses SSL
@ -373,3 +381,148 @@ ssl_issuer_dn(PG_FUNCTION_ARGS)
PG_RETURN_NULL(); PG_RETURN_NULL();
return X509_NAME_to_text(X509_get_issuer_name(MyProcPort->peer)); return X509_NAME_to_text(X509_get_issuer_name(MyProcPort->peer));
} }
/*
* Returns information about available SSL extensions.
*
* Returns setof record made of the following values:
* - name of the extension.
* - value of the extension.
* - critical status of the extension.
*/
PG_FUNCTION_INFO_V1(ssl_extension_info);
Datum
ssl_extension_info(PG_FUNCTION_ARGS)
{
X509 *cert = MyProcPort->peer;
FuncCallContext *funcctx;
int call_cntr;
int max_calls;
MemoryContext oldcontext;
SSLExtensionInfoContext *fctx;
STACK_OF(X509_EXTENSION) *ext_stack = NULL;
if (SRF_IS_FIRSTCALL())
{
TupleDesc tupdesc;
/* create a function context for cross-call persistence */
funcctx = SRF_FIRSTCALL_INIT();
/*
* Switch to memory context appropriate for multiple function calls
*/
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
/* Create a user function context for cross-call persistence */
fctx = (SSLExtensionInfoContext *) palloc(sizeof(SSLExtensionInfoContext));
/* Construct tuple descriptor */
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("function returning record called in context that cannot accept type record")));
fctx->tupdesc = BlessTupleDesc(tupdesc);
/* Get all extensions of certificate */
if (cert && cert->cert_info)
ext_stack = cert->cert_info->extensions;
/* Set max_calls as a count of extensions in certificate */
max_calls = cert != NULL ? X509_get_ext_count(cert) : 0;
if (cert != NULL &&
ext_stack != NULL &&
max_calls > 0)
{
/* got results, keep track of them */
funcctx->max_calls = max_calls;
funcctx->user_fctx = fctx;
}
else
{
/* fast track when no results */
MemoryContextSwitchTo(oldcontext);
SRF_RETURN_DONE(funcctx);
}
MemoryContextSwitchTo(oldcontext);
}
/* stuff done on every call of the function */
funcctx = SRF_PERCALL_SETUP();
/*
* Initialize per-call variables.
*/
call_cntr = funcctx->call_cntr;
max_calls = funcctx->max_calls;
fctx = funcctx->user_fctx;
ext_stack = cert->cert_info->extensions;
/* do while there are more left to send */
if (call_cntr < max_calls)
{
Datum values[3];
bool nulls[3];
char *buf;
HeapTuple tuple;
Datum result;
BIO *membuf;
X509_EXTENSION *ext;
ASN1_OBJECT *obj;
int nid;
int len;
/* need a BIO for this */
membuf = BIO_new(BIO_s_mem());
if (membuf == NULL)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("could not create OpenSSL BIO structure")));
/* Get the extension from the certificate */
ext = sk_X509_EXTENSION_value(ext_stack, call_cntr);
obj = X509_EXTENSION_get_object(ext);
/* Get the extension name */
nid = OBJ_obj2nid(obj);
if (nid == NID_undef)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("unknown OpenSSL extension in certificate at position %d",
call_cntr)));
values[0] = CStringGetTextDatum(OBJ_nid2sn(nid));
nulls[0] = false;
/* Get the extension value */
if (X509V3_EXT_print(membuf, ext, 0, 0) <= 0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("could not print extension value in certificate at position %d",
call_cntr)));
len = BIO_get_mem_data(membuf, &buf);
values[1] = PointerGetDatum(cstring_to_text_with_len(buf, len));
nulls[1] = false;
/* Get critical status */
values[2] = BoolGetDatum(X509_EXTENSION_get_critical(ext));
nulls[2] = false;
/* Build tuple */
tuple = heap_form_tuple(fctx->tupdesc, values, nulls);
result = HeapTupleGetDatum(tuple);
if (BIO_free(membuf) != 1)
elog(ERROR, "could not free OpenSSL BIO structure");
SRF_RETURN_NEXT(funcctx, result);
}
/* Do when there is no more left */
SRF_RETURN_DONE(funcctx);
}

View File

@ -1,5 +1,5 @@
# sslinfo extension # sslinfo extension
comment = 'information about SSL certificates' comment = 'information about SSL certificates'
default_version = '1.0' default_version = '1.1'
module_pathname = '$libdir/sslinfo' module_pathname = '$libdir/sslinfo'
relocatable = true relocatable = true

View File

@ -219,6 +219,21 @@ emailAddress
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term>
<function>ssl_extension_info() returns setof record</function>
<indexterm>
<primary>ssl_extension_info</primary>
</indexterm>
</term>
<listitem>
<para>
Provide information about extensions of client certificate: extension name,
extension value, and if it is a critical extension.
</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</sect2> </sect2>
@ -229,6 +244,10 @@ emailAddress
Victor Wagner <email>vitus@cryptocom.ru</email>, Cryptocom LTD Victor Wagner <email>vitus@cryptocom.ru</email>, Cryptocom LTD
</para> </para>
<para>
Dmitry Voronin <email>carriingfate92@yandex.ru</email>
</para>
<para> <para>
E-Mail of Cryptocom OpenSSL development group: E-Mail of Cryptocom OpenSSL development group:
<email>openssl@cryptocom.ru</email> <email>openssl@cryptocom.ru</email>