2 * module for PostgreSQL to access client SSL certificate information
4 * Written by Victor B. Wagner <vitus@cryptocom.ru>, Cryptocom LTD
5 * This file is distributed under BSD-style license.
7 * contrib/sslinfo/sslinfo.c
12#include <openssl/x509.h>
13#include <openssl/x509v3.h>
14#include <openssl/asn1.h>
31 * Function context for data persisting over repeated calls.
39 * Indicates whether current session uses SSL
41 * Function has no arguments. Returns bool. True if current session
42 * is SSL session and false if it is local or non-ssl session.
53 * Returns SSL version currently in use.
73 * Returns SSL cipher currently in use.
93 * Indicates whether current client provided a certificate
95 * Function has no arguments. Returns bool. True if current session
96 * is SSL session and client certificate is verified, otherwise false.
107 * Returns serial number of certificate used to establish current
110 * Function has no arguments. It returns the certificate serial
111 * number as numeric or null if current session doesn't use SSL or if
112 * SSL connection is established without sending client certificate.
138 * Converts OpenSSL ASN1_STRING structure into text
140 * Converts ASN1_STRING into text, converting all the characters into
141 * current database encoding if possible. Any invalid characters are
142 * replaced by question marks.
144 * Parameter: str - OpenSSL ASN1_STRING structure. Memory management
145 * of this structure is responsibility of caller.
147 * Returns Datum, which can be directly returned from a C language SQL
160 membuf = BIO_new(BIO_s_mem());
163 (
errcode(ERRCODE_OUT_OF_MEMORY),
164 errmsg(
"could not create OpenSSL BIO structure")));
165 (void) BIO_set_close(membuf, BIO_CLOSE);
166 ASN1_STRING_print_ex(membuf,
str,
167 ((ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB)
168 | ASN1_STRFLGS_UTF8_CONVERT));
169 /* ensure null termination of the BIO's content */
171 BIO_write(membuf, &nullterm, 1);
172 size = BIO_get_mem_data(membuf, &sp);
177 if (BIO_free(membuf) != 1)
178 elog(
ERROR,
"could not free OpenSSL BIO structure");
185 * Returns specified field of specified X509_NAME structure
187 * Common part of ssl_client_dn and ssl_issuer_dn functions.
189 * Parameter: X509_NAME *name - either subject or issuer of certificate
190 * Parameter: text fieldName - field name string like 'CN' or commonName
191 * to be looked up in the OpenSSL ASN1 OID database
193 * Returns result of ASN1_STRING_to_text applied to appropriate
199 char *string_fieldname;
205 nid = OBJ_txt2nid(string_fieldname);
206 if (nid == NID_undef)
208 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
209 errmsg(
"invalid X.509 field name: \"%s\"",
211 pfree(string_fieldname);
212 index = X509_NAME_get_index_by_NID(
name, nid, -1);
215 data = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(
name,
index));
221 * Returns specified field of client certificate distinguished name
223 * Receives field name (like 'commonName' and 'emailAddress') and
224 * returns appropriate part of certificate subject converted into
227 * Parameter: fieldname text - will be looked up in OpenSSL object
228 * identifier database
230 * Returns text string with appropriate value.
232 * Throws an error if argument cannot be converted into ASN1 OID by
233 * OpenSSL. Returns null if no client certificate is present, or if
234 * there is no field with such name in the certificate.
256 * Returns specified field of client certificate issuer name
258 * Receives field name (like 'commonName' and 'emailAddress') and
259 * returns appropriate part of certificate subject converted into
262 * Parameter: fieldname text - would be looked up in OpenSSL object
263 * identifier database
265 * Returns text string with appropriate value.
267 * Throws an error if argument cannot be converted into ASN1 OID by
268 * OpenSSL. Returns null if no client certificate is present, or if
269 * there is no field with such name in the certificate.
291 * Returns current client certificate subject as one string
293 * This function returns distinguished name (subject) of the client
294 * certificate used in the current SSL connection, converting it into
295 * the current database encoding.
297 * Returns text datum.
318 * Returns current client certificate issuer as one string
320 * This function returns issuer's distinguished name of the client
321 * certificate used in the current SSL connection, converting it into
322 * the current database encoding.
324 * Returns text datum.
345 * Returns information about available SSL extensions.
347 * Returns setof record made of the following values:
348 * - name of the extension.
349 * - value of the extension.
350 * - critical status of the extension.
368 /* create a function context for cross-call persistence */
372 * Switch to memory context appropriate for multiple function calls
376 /* Create a user function context for cross-call persistence */
379 /* Construct tuple descriptor */
382 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
383 errmsg(
"function returning record called in context that cannot accept type record")));
386 /* Set max_calls as a count of extensions in certificate */
387 max_calls = cert != NULL ? X509_get_ext_count(cert) : 0;
391 /* got results, keep track of them */
397 /* fast track when no results */
405 /* stuff done on every call of the function */
409 * Initialize per-call variables.
415 /* do while there are more left to send */
416 if (call_cntr < max_calls)
429 /* need a BIO for this */
430 membuf = BIO_new(BIO_s_mem());
433 (
errcode(ERRCODE_OUT_OF_MEMORY),
434 errmsg(
"could not create OpenSSL BIO structure")));
436 /* Get the extension from the certificate */
437 ext = X509_get_ext(cert, call_cntr);
438 obj = X509_EXTENSION_get_object(ext);
440 /* Get the extension name */
441 nid = OBJ_obj2nid(obj);
442 if (nid == NID_undef)
444 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
445 errmsg(
"unknown OpenSSL extension in certificate at position %d",
450 /* Get the extension value */
451 if (X509V3_EXT_print(membuf, ext, 0, 0) <= 0)
453 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
454 errmsg(
"could not print extension value in certificate at position %d",
456 len = BIO_get_mem_data(membuf, &
buf);
460 /* Get critical status */
468 if (BIO_free(membuf) != 1)
469 elog(
ERROR,
"could not free OpenSSL BIO structure");
Datum numeric_in(PG_FUNCTION_ARGS)
const char * be_tls_get_version(Port *port)
const char * be_tls_get_cipher(Port *port)
void be_tls_get_peer_serial(Port *port, char *ptr, size_t len)
void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len)
void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
static Datum values[MAXATTR]
#define CStringGetTextDatum(s)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
TupleDesc BlessTupleDesc(TupleDesc tupdesc)
#define PG_GETARG_TEXT_PP(n)
#define PG_RETURN_TEXT_P(x)
#define DirectFunctionCall3(func, arg1, arg2, arg3)
#define PG_RETURN_BOOL(x)
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
#define SRF_IS_FIRSTCALL()
#define SRF_PERCALL_SETUP()
#define SRF_RETURN_NEXT(_funcctx, _result)
#define SRF_FIRSTCALL_INIT()
static Datum HeapTupleGetDatum(const HeapTupleData *tuple)
#define SRF_RETURN_DONE(_funcctx)
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
char * pg_any_to_server(const char *s, int len, int encoding)
void pfree(void *pointer)
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
static Datum PointerGetDatum(const void *X)
static Datum BoolGetDatum(bool X)
static Datum ObjectIdGetDatum(Oid X)
static Datum CStringGetDatum(const char *X)
static Datum Int32GetDatum(int32 X)
static Datum X509_NAME_field_to_text(X509_NAME *name, text *fieldName)
Datum ssl_version(PG_FUNCTION_ARGS)
Datum ssl_client_serial(PG_FUNCTION_ARGS)
Datum ssl_issuer_field(PG_FUNCTION_ARGS)
static Datum ASN1_STRING_to_text(ASN1_STRING *str)
Datum ssl_issuer_dn(PG_FUNCTION_ARGS)
Datum ssl_client_dn(PG_FUNCTION_ARGS)
Datum ssl_extension_info(PG_FUNCTION_ARGS)
Datum ssl_cipher(PG_FUNCTION_ARGS)
PG_FUNCTION_INFO_V1(ssl_is_used)
Datum ssl_client_dn_field(PG_FUNCTION_ARGS)
Datum ssl_client_cert_present(PG_FUNCTION_ARGS)
PG_MODULE_MAGIC_EXT(.name="sslinfo",.version=PG_VERSION)
Datum ssl_is_used(PG_FUNCTION_ARGS)
MemoryContext multi_call_memory_ctx
text * cstring_to_text_with_len(const char *s, int len)
text * cstring_to_text(const char *s)
char * text_to_cstring(const text *t)