mirror of https://github.com/postgres/postgres
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:
parent
582fbffb0c
commit
49124613f1
|
@ -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
|
||||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue