diff options
Diffstat (limited to 'contrib/sslinfo/sslinfo.c')
-rw-r--r-- | contrib/sslinfo/sslinfo.c | 165 |
1 files changed, 159 insertions, 6 deletions
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c index 6e230052c9d..e00f8a2663c 100644 --- a/contrib/sslinfo/sslinfo.c +++ b/contrib/sslinfo/sslinfo.c @@ -8,22 +8,30 @@ */ #include "postgres.h" -#include "fmgr.h" -#include "utils/numeric.h" -#include "libpq/libpq-be.h" -#include "miscadmin.h" -#include "utils/builtins.h" -#include "mb/pg_wchar.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" + PG_MODULE_MAGIC; 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); +} |