Hello,
I make an a patch, which adds 4 functions to sslinfo extension module:
1) ssl_extension_names() --- get short names of X509v3 extensions from client certificate and it's values; 2) ssl_extension_value(text) --- get value of extension from certificate (argument --- short name of extension); 3) ssl_extension_is_critical(text) --- returns true, if extension is critical and false, if is not (argument --- short name of extension).
You can view some information of certificate's extensions via those functions. What do you think about it?
--
Best regards, Dmitry Voronin
*** a/contrib/sslinfo/sslinfo.c --- b/contrib/sslinfo/sslinfo.c *************** *** 14,21 **** --- 14,23 ---- #include "miscadmin.h" #include "utils/builtins.h" #include "mb/pg_wchar.h" + #include "funcapi.h" #include <openssl/x509.h> + #include <openssl/x509v3.h> #include <openssl/asn1.h> PG_MODULE_MAGIC; *************** *** 23,28 **** PG_MODULE_MAGIC; --- 25,31 ---- 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); + int get_extension(X509 *cert, const char *ext_name, X509_EXTENSION **extension); /* *************** *** 354,356 **** ssl_issuer_dn(PG_FUNCTION_ARGS) --- 357,581 ---- PG_RETURN_NULL(); return X509_NAME_to_text(X509_get_issuer_name(MyProcPort->peer)); } + + + /* + * Returns extension object by given certificate and extension's name. + * + * Try to get extension from certificate by extension's name. + * We returns at extension param pointer to X509_EXTENSION. + * + * Returns 0, if we have found extension in certificate and 1, if we not. + */ + int get_extension(X509* cert, const char *ext_name, X509_EXTENSION **extension) + { + int nid = 0; + int loc = 0; + + /* try to convert extension name to ObjectID */ + nid = OBJ_txt2nid(ext_name); + /* Not success ? */ + if (nid == NID_undef) + return 1; + + loc = X509_get_ext_by_NID(cert, nid, -1); + + /* palloc memory for extension and copy it */ + *extension = (X509_EXTENSION *) palloc(sizeof(X509_EXTENSION *)); + memcpy(*extension, X509_get_ext(cert, loc), sizeof(X509_EXTENSION)); + + return 0; + } + + + /* Returns value of extension + * + * This function returns value of extension by given name in client certificate. + * + * Returns text datum. + */ + PG_FUNCTION_INFO_V1(ssl_extension_value); + Datum + ssl_extension_value(PG_FUNCTION_ARGS) + { + X509 *cert = MyProcPort->peer; + X509_EXTENSION *ext = NULL; + char *ext_name = text_to_cstring(PG_GETARG_TEXT_P(0)); + BIO *membuf = NULL; + char *val = NULL; + char nullterm = '\0'; + int error = 0; + + /* If we have no ssl security */ + if (cert == NULL) + PG_RETURN_NULL(); + + /* If extension's converting from text name to extension's OID failed (return NID_undef) */ + if (OBJ_txt2nid(ext_name) == NID_undef) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Unknown extension name \"%s\"", ext_name))); + + /* Extension's name is correct, try to get extension object from certificate */ + error = get_extension(cert, ext_name, &ext); + + /* Not found? */ + if (error) + PG_RETURN_NULL(); + + /* Print extension to BIO */ + membuf = BIO_new(BIO_s_mem()); + X509V3_EXT_print(membuf, ext, 0, 0); + BIO_write(membuf, &nullterm, 1); + BIO_get_mem_data(membuf, &val); + + /* Copy value */ + val = pstrdup(val); + + /* Clear BIO */ + BIO_free(membuf); + + /* free extension */ + if (ext) + pfree(ext); + + PG_RETURN_TEXT_P(cstring_to_text(val)); + } + + + /* Returns status of extension + * + * Returns true, if extension is critical and false, if it is not. + * + * Returns bool datum. + */ + PG_FUNCTION_INFO_V1(ssl_extension_is_critical); + Datum + ssl_extension_is_critical(PG_FUNCTION_ARGS) + { + X509 *cert = MyProcPort->peer; + X509_EXTENSION *ext = NULL; + char *ext_name = text_to_cstring(PG_GETARG_TEXT_P(0)); + int critical; + int error = 0; + + /* If we have no ssl security */ + if (cert == NULL) + PG_RETURN_NULL(); + + /* If extension's converting from text name to extension's OID failed (return NID_undef) */ + if (OBJ_txt2nid(ext_name) == NID_undef) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Unknown extension name \"%s\"", ext_name))); + + /* Extension's name is correct, try to get extension object from certificate */ + error = get_extension(cert, ext_name, &ext); + + /* Not found? */ + if (error) + PG_RETURN_NULL(); + + critical = X509_EXTENSION_get_critical(ext); + + /* free extension */ + if (ext) + pfree(ext); + + PG_RETURN_BOOL(critical); + } + + + /* Returns key-value pairs of extension name and it's value + * + * This function print extensions of client certificate at table form (extension's name and it's value). + * + * Returns setof text datum. + */ + PG_FUNCTION_INFO_V1(ssl_extension_names); + Datum + ssl_extension_names(PG_FUNCTION_ARGS) + { + X509 *cert = MyProcPort->peer; + FuncCallContext *funcctx; + STACK_OF(X509_EXTENSION) *ext_stack = NULL; + int call; + int max_calls; + TupleDesc tupdesc; + AttInMetadata *attinmeta; + MemoryContext oldcontext; + char **values; + HeapTuple tuple; + int nid; + X509_EXTENSION *ext = NULL; + ASN1_OBJECT *obj = NULL; + BIO *membuf = NULL; + char nullterm = '\0'; + + /* If we have no ssl security */ + if (cert == NULL) + PG_RETURN_NULL(); + + /* Get all extensions of certificate */ + ext_stack = cert->cert_info->extensions; + + /* Certificate does not have extensions? */ + if (ext_stack == NULL) + PG_RETURN_NULL(); + + if (SRF_IS_FIRSTCALL()) + { + funcctx = SRF_FIRSTCALL_INIT(); + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + /* Set mac_calls as a count of extensions in certificate */ + funcctx->max_calls = X509_get_ext_count(cert); + + 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"))); + + attinmeta = TupleDescGetAttInMetadata(tupdesc); + funcctx->attinmeta = attinmeta; + + MemoryContextSwitchTo(oldcontext); + } + + funcctx = SRF_PERCALL_SETUP(); + call = funcctx->call_cntr; + max_calls = funcctx->max_calls; + attinmeta = funcctx->attinmeta; + + if (call < max_calls) + { + values = (char **) palloc(2 * sizeof(char *)); + + /* Get extension from certificate */ + ext = sk_X509_EXTENSION_value(ext_stack, call); + obj = X509_EXTENSION_get_object(ext); + nid = OBJ_obj2nid(obj); + + if (nid == NID_undef) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Unknown extension name \"%s\"", ext_name))); + + /* Set extension name as first value in array */ + values[0] = (char *) OBJ_nid2sn(nid); + + /* Print extension's value and set as second value in array */ + membuf = BIO_new(BIO_s_mem()); + X509V3_EXT_print(membuf, ext, 0, 0); + BIO_write(membuf, &nullterm, 1); + BIO_get_mem_data(membuf, &values[1]); + + /* Build tuple */ + tuple = BuildTupleFromCStrings(attinmeta, values); + + BIO_free(membuf); + + SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); + } + SRF_RETURN_DONE(funcctx); + } *** a/contrib/sslinfo/sslinfo.control --- b/contrib/sslinfo/sslinfo.control *************** *** 1,5 **** # sslinfo extension comment = 'information about SSL certificates' ! default_version = '1.0' module_pathname = '$libdir/sslinfo' relocatable = true --- 1,5 ---- # sslinfo extension comment = 'information about SSL certificates' ! default_version = '1.1' module_pathname = '$libdir/sslinfo' relocatable = true *** a/doc/src/sgml/sslinfo.sgml --- b/doc/src/sgml/sslinfo.sgml *************** *** 219,224 **** emailAddress --- 219,266 ---- </para> </listitem> </varlistentry> + + <varlistentry> + <term> + <function>ssl_extension_value(extension_name text) returns text</function> + <indexterm> + <primary>ssl_extension_value</primary> + </indexterm> + </term> + <listitem> + <para> + Returns value of extension by given name in client certificate. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <function>ssl_extension_is_critical(extension_name text) returns boolean</function> + <indexterm> + <primary>ssl_extension_is_critical</primary> + </indexterm> + </term> + <listitem> + <para> + Returns TRUE if extension is critical and FALSE if not. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <function>ssl_extension_names() returns SETOF text </function> + <indexterm> + <primary>ssl_extension_names</primary> + </indexterm> + </term> + <listitem> + <para> + Print extensions of client certificate at table form (extension's name and it's value). + </para> + </listitem> + </varlistentry> </variablelist> </sect2> *************** *** 228,233 **** emailAddress --- 270,279 ---- <para> Victor Wagner <email>vi...@cryptocom.ru</email>, Cryptocom LTD </para> + + <para> + Extension's functions: Dmitry Voronin <email>carriingfat...@yandex.ru</email> + </para> <para> E-Mail of Cryptocom OpenSSL development group:
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers