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)
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"
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
\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
@ -38,3 +38,11 @@ LANGUAGE C STRICT;
CREATE FUNCTION ssl_issuer_dn() RETURNS text
AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
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 "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 "miscadmin.h"
#include "utils/builtins.h"
#include "mb/pg_wchar.h"
#include <openssl/x509.h>
#include <openssl/asn1.h>
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 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
@ -373,3 +381,148 @@ ssl_issuer_dn(PG_FUNCTION_ARGS)
PG_RETURN_NULL();
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
comment = 'information about SSL certificates'
default_version = '1.0'
default_version = '1.1'
module_pathname = '$libdir/sslinfo'
relocatable = true

View File

@ -219,6 +219,21 @@ emailAddress
</para>
</listitem>
</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>
</sect2>
@ -229,6 +244,10 @@ emailAddress
Victor Wagner <email>vitus@cryptocom.ru</email>, Cryptocom LTD
</para>
<para>
Dmitry Voronin <email>carriingfate92@yandex.ru</email>
</para>
<para>
E-Mail of Cryptocom OpenSSL development group:
<email>openssl@cryptocom.ru</email>