PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
sslinfo.c
Go to the documentation of this file.
1/*
2 * module for PostgreSQL to access client SSL certificate information
3 *
4 * Written by Victor B. Wagner <vitus@cryptocom.ru>, Cryptocom LTD
5 * This file is distributed under BSD-style license.
6 *
7 * contrib/sslinfo/sslinfo.c
8 */
9
10#include "postgres.h"
11
12#include <openssl/x509.h>
13#include <openssl/x509v3.h>
14#include <openssl/asn1.h>
15
16#include "access/htup_details.h"
17#include "funcapi.h"
18#include "libpq/libpq-be.h"
19#include "miscadmin.h"
20#include "utils/builtins.h"
21
22/*
23 * On Windows, <wincrypt.h> includes a #define for X509_NAME, which breaks our
24 * ability to use OpenSSL's version of that symbol if <wincrypt.h> is pulled
25 * in after <openssl/ssl.h> ... and, at least on some builds, it is. We
26 * can't reliably fix that by re-ordering #includes, because libpq/libpq-be.h
27 * #includes <openssl/ssl.h>. Instead, just zap the #define again here.
28 */
29#ifdef X509_NAME
30#undef X509_NAME
31#endif
32
34
35static Datum X509_NAME_field_to_text(X509_NAME *name, text *fieldName);
36static Datum ASN1_STRING_to_text(ASN1_STRING *str);
37
38/*
39 * Function context for data persisting over repeated calls.
40 */
41typedef struct
42{
45
46/*
47 * Indicates whether current session uses SSL
48 *
49 * Function has no arguments. Returns bool. True if current session
50 * is SSL session and false if it is local or non-ssl session.
51 */
55{
57}
58
59
60/*
61 * Returns SSL version currently in use.
62 */
66{
67 const char *version;
68
71
73 if (version == NULL)
75
77}
78
79
80/*
81 * Returns SSL cipher currently in use.
82 */
86{
87 const char *cipher;
88
91
93 if (cipher == NULL)
95
97}
98
99
100/*
101 * Indicates whether current client provided a certificate
102 *
103 * Function has no arguments. Returns bool. True if current session
104 * is SSL session and client certificate is verified, otherwise false.
105 */
107Datum
109{
111}
112
113
114/*
115 * Returns serial number of certificate used to establish current
116 * session
117 *
118 * Function has no arguments. It returns the certificate serial
119 * number as numeric or null if current session doesn't use SSL or if
120 * SSL connection is established without sending client certificate.
121 */
123Datum
125{
126 char decimal[NAMEDATALEN];
127 Datum result;
128
131
133
134 if (!*decimal)
136
140 Int32GetDatum(-1));
141 return result;
142}
143
144
145/*
146 * Converts OpenSSL ASN1_STRING structure into text
147 *
148 * Converts ASN1_STRING into text, converting all the characters into
149 * current database encoding if possible. Any invalid characters are
150 * replaced by question marks.
151 *
152 * Parameter: str - OpenSSL ASN1_STRING structure. Memory management
153 * of this structure is responsibility of caller.
154 *
155 * Returns Datum, which can be directly returned from a C language SQL
156 * function.
157 */
158static Datum
160{
161 BIO *membuf;
162 size_t size;
163 char nullterm;
164 char *sp;
165 char *dp;
166 text *result;
167
168 membuf = BIO_new(BIO_s_mem());
169 if (membuf == NULL)
171 (errcode(ERRCODE_OUT_OF_MEMORY),
172 errmsg("could not create OpenSSL BIO structure")));
173 (void) BIO_set_close(membuf, BIO_CLOSE);
174 ASN1_STRING_print_ex(membuf, str,
175 ((ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB)
176 | ASN1_STRFLGS_UTF8_CONVERT));
177 /* ensure null termination of the BIO's content */
178 nullterm = '\0';
179 BIO_write(membuf, &nullterm, 1);
180 size = BIO_get_mem_data(membuf, &sp);
181 dp = pg_any_to_server(sp, size - 1, PG_UTF8);
182 result = cstring_to_text(dp);
183 if (dp != sp)
184 pfree(dp);
185 if (BIO_free(membuf) != 1)
186 elog(ERROR, "could not free OpenSSL BIO structure");
187
188 PG_RETURN_TEXT_P(result);
189}
190
191
192/*
193 * Returns specified field of specified X509_NAME structure
194 *
195 * Common part of ssl_client_dn and ssl_issuer_dn functions.
196 *
197 * Parameter: X509_NAME *name - either subject or issuer of certificate
198 * Parameter: text fieldName - field name string like 'CN' or commonName
199 * to be looked up in the OpenSSL ASN1 OID database
200 *
201 * Returns result of ASN1_STRING_to_text applied to appropriate
202 * part of name
203 */
204static Datum
205X509_NAME_field_to_text(X509_NAME *name, text *fieldName)
206{
207 char *string_fieldname;
208 int nid,
209 index;
210 ASN1_STRING *data;
211
212 string_fieldname = text_to_cstring(fieldName);
213 nid = OBJ_txt2nid(string_fieldname);
214 if (nid == NID_undef)
216 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
217 errmsg("invalid X.509 field name: \"%s\"",
218 string_fieldname)));
219 pfree(string_fieldname);
220 index = X509_NAME_get_index_by_NID(name, nid, -1);
221 if (index < 0)
222 return (Datum) 0;
223 data = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, index));
225}
226
227
228/*
229 * Returns specified field of client certificate distinguished name
230 *
231 * Receives field name (like 'commonName' and 'emailAddress') and
232 * returns appropriate part of certificate subject converted into
233 * database encoding.
234 *
235 * Parameter: fieldname text - will be looked up in OpenSSL object
236 * identifier database
237 *
238 * Returns text string with appropriate value.
239 *
240 * Throws an error if argument cannot be converted into ASN1 OID by
241 * OpenSSL. Returns null if no client certificate is present, or if
242 * there is no field with such name in the certificate.
243 */
245Datum
247{
248 text *fieldname = PG_GETARG_TEXT_PP(0);
249 Datum result;
250
253
254 result = X509_NAME_field_to_text(X509_get_subject_name(MyProcPort->peer), fieldname);
255
256 if (!result)
258 else
259 return result;
260}
261
262
263/*
264 * Returns specified field of client certificate issuer name
265 *
266 * Receives field name (like 'commonName' and 'emailAddress') and
267 * returns appropriate part of certificate subject converted into
268 * database encoding.
269 *
270 * Parameter: fieldname text - would be looked up in OpenSSL object
271 * identifier database
272 *
273 * Returns text string with appropriate value.
274 *
275 * Throws an error if argument cannot be converted into ASN1 OID by
276 * OpenSSL. Returns null if no client certificate is present, or if
277 * there is no field with such name in the certificate.
278 */
280Datum
282{
283 text *fieldname = PG_GETARG_TEXT_PP(0);
284 Datum result;
285
286 if (!(MyProcPort->peer))
288
289 result = X509_NAME_field_to_text(X509_get_issuer_name(MyProcPort->peer), fieldname);
290
291 if (!result)
293 else
294 return result;
295}
296
297
298/*
299 * Returns current client certificate subject as one string
300 *
301 * This function returns distinguished name (subject) of the client
302 * certificate used in the current SSL connection, converting it into
303 * the current database encoding.
304 *
305 * Returns text datum.
306 */
308Datum
310{
311 char subject[NAMEDATALEN];
312
315
317
318 if (!*subject)
320
322}
323
324
325/*
326 * Returns current client certificate issuer as one string
327 *
328 * This function returns issuer's distinguished name of the client
329 * certificate used in the current SSL connection, converting it into
330 * the current database encoding.
331 *
332 * Returns text datum.
333 */
335Datum
337{
338 char issuer[NAMEDATALEN];
339
342
344
345 if (!*issuer)
347
349}
350
351
352/*
353 * Returns information about available SSL extensions.
354 *
355 * Returns setof record made of the following values:
356 * - name of the extension.
357 * - value of the extension.
358 * - critical status of the extension.
359 */
361Datum
363{
364 X509 *cert = MyProcPort->peer;
365 FuncCallContext *funcctx;
366 int call_cntr;
367 int max_calls;
368 MemoryContext oldcontext;
370
371 if (SRF_IS_FIRSTCALL())
372 {
373
374 TupleDesc tupdesc;
375
376 /* create a function context for cross-call persistence */
377 funcctx = SRF_FIRSTCALL_INIT();
378
379 /*
380 * Switch to memory context appropriate for multiple function calls
381 */
382 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
383
384 /* Create a user function context for cross-call persistence */
386
387 /* Construct tuple descriptor */
388 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
390 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
391 errmsg("function returning record called in context that cannot accept type record")));
392 fctx->tupdesc = BlessTupleDesc(tupdesc);
393
394 /* Set max_calls as a count of extensions in certificate */
395 max_calls = cert != NULL ? X509_get_ext_count(cert) : 0;
396
397 if (max_calls > 0)
398 {
399 /* got results, keep track of them */
400 funcctx->max_calls = max_calls;
401 funcctx->user_fctx = fctx;
402 }
403 else
404 {
405 /* fast track when no results */
406 MemoryContextSwitchTo(oldcontext);
407 SRF_RETURN_DONE(funcctx);
408 }
409
410 MemoryContextSwitchTo(oldcontext);
411 }
412
413 /* stuff done on every call of the function */
414 funcctx = SRF_PERCALL_SETUP();
415
416 /*
417 * Initialize per-call variables.
418 */
419 call_cntr = funcctx->call_cntr;
420 max_calls = funcctx->max_calls;
421 fctx = funcctx->user_fctx;
422
423 /* do while there are more left to send */
424 if (call_cntr < max_calls)
425 {
426 Datum values[3];
427 bool nulls[3];
428 char *buf;
429 HeapTuple tuple;
430 Datum result;
431 BIO *membuf;
432 X509_EXTENSION *ext;
433 ASN1_OBJECT *obj;
434 int nid;
435 int len;
436
437 /* need a BIO for this */
438 membuf = BIO_new(BIO_s_mem());
439 if (membuf == NULL)
441 (errcode(ERRCODE_OUT_OF_MEMORY),
442 errmsg("could not create OpenSSL BIO structure")));
443
444 /* Get the extension from the certificate */
445 ext = X509_get_ext(cert, call_cntr);
446 obj = X509_EXTENSION_get_object(ext);
447
448 /* Get the extension name */
449 nid = OBJ_obj2nid(obj);
450 if (nid == NID_undef)
452 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
453 errmsg("unknown OpenSSL extension in certificate at position %d",
454 call_cntr)));
455 values[0] = CStringGetTextDatum(OBJ_nid2sn(nid));
456 nulls[0] = false;
457
458 /* Get the extension value */
459 if (X509V3_EXT_print(membuf, ext, 0, 0) <= 0)
461 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
462 errmsg("could not print extension value in certificate at position %d",
463 call_cntr)));
464 len = BIO_get_mem_data(membuf, &buf);
466 nulls[1] = false;
467
468 /* Get critical status */
469 values[2] = BoolGetDatum(X509_EXTENSION_get_critical(ext));
470 nulls[2] = false;
471
472 /* Build tuple */
473 tuple = heap_form_tuple(fctx->tupdesc, values, nulls);
474 result = HeapTupleGetDatum(tuple);
475
476 if (BIO_free(membuf) != 1)
477 elog(ERROR, "could not free OpenSSL BIO structure");
478
479 SRF_RETURN_NEXT(funcctx, result);
480 }
481
482 /* All done */
483 SRF_RETURN_DONE(funcctx);
484}
Datum numeric_in(PG_FUNCTION_ARGS)
Definition: numeric.c:637
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]
Definition: bootstrap.c:151
#define CStringGetTextDatum(s)
Definition: builtins.h:97
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define ereport(elevel,...)
Definition: elog.h:149
TupleDesc BlessTupleDesc(TupleDesc tupdesc)
Definition: execTuples.c:2258
#define PG_GETARG_TEXT_PP(n)
Definition: fmgr.h:309
#define PG_RETURN_NULL()
Definition: fmgr.h:345
#define PG_RETURN_TEXT_P(x)
Definition: fmgr.h:372
#define DirectFunctionCall3(func, arg1, arg2, arg3)
Definition: fmgr.h:645
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition: funcapi.c:276
#define SRF_IS_FIRSTCALL()
Definition: funcapi.h:304
#define SRF_PERCALL_SETUP()
Definition: funcapi.h:308
@ TYPEFUNC_COMPOSITE
Definition: funcapi.h:149
#define SRF_RETURN_NEXT(_funcctx, _result)
Definition: funcapi.h:310
#define SRF_FIRSTCALL_INIT()
Definition: funcapi.h:306
static Datum HeapTupleGetDatum(const HeapTupleData *tuple)
Definition: funcapi.h:230
#define SRF_RETURN_DONE(_funcctx)
Definition: funcapi.h:328
struct Port * MyProcPort
Definition: globals.c:50
const char * str
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition: heaptuple.c:1117
char * pg_any_to_server(const char *s, int len, int encoding)
Definition: mbutils.c:676
void pfree(void *pointer)
Definition: mcxt.c:1524
void * palloc(Size size)
Definition: mcxt.c:1317
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
#define NAMEDATALEN
const void size_t len
const void * data
static char * buf
Definition: pg_test_fsync.c:72
@ PG_UTF8
Definition: pg_wchar.h:232
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:327
uintptr_t Datum
Definition: postgres.h:69
static Datum BoolGetDatum(bool X)
Definition: postgres.h:107
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:257
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:355
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:217
static Datum X509_NAME_field_to_text(X509_NAME *name, text *fieldName)
Definition: sslinfo.c:205
Datum ssl_version(PG_FUNCTION_ARGS)
Definition: sslinfo.c:65
Datum ssl_client_serial(PG_FUNCTION_ARGS)
Definition: sslinfo.c:124
Datum ssl_issuer_field(PG_FUNCTION_ARGS)
Definition: sslinfo.c:281
PG_MODULE_MAGIC
Definition: sslinfo.c:33
static Datum ASN1_STRING_to_text(ASN1_STRING *str)
Definition: sslinfo.c:159
Datum ssl_issuer_dn(PG_FUNCTION_ARGS)
Definition: sslinfo.c:336
Datum ssl_client_dn(PG_FUNCTION_ARGS)
Definition: sslinfo.c:309
Datum ssl_extension_info(PG_FUNCTION_ARGS)
Definition: sslinfo.c:362
Datum ssl_cipher(PG_FUNCTION_ARGS)
Definition: sslinfo.c:85
PG_FUNCTION_INFO_V1(ssl_is_used)
Datum ssl_client_dn_field(PG_FUNCTION_ARGS)
Definition: sslinfo.c:246
Datum ssl_client_cert_present(PG_FUNCTION_ARGS)
Definition: sslinfo.c:108
Datum ssl_is_used(PG_FUNCTION_ARGS)
Definition: sslinfo.c:54
void * user_fctx
Definition: funcapi.h:82
uint64 max_calls
Definition: funcapi.h:74
uint64 call_cntr
Definition: funcapi.h:65
MemoryContext multi_call_memory_ctx
Definition: funcapi.h:101
bool ssl_in_use
Definition: libpq-be.h:211
bool peer_cert_valid
Definition: libpq-be.h:214
void * peer
Definition: libpq-be.h:229
Definition: type.h:96
Definition: c.h:658
text * cstring_to_text_with_len(const char *s, int len)
Definition: varlena.c:204
text * cstring_to_text(const char *s)
Definition: varlena.c:192
char * text_to_cstring(const text *t)
Definition: varlena.c:225
const char * name