PostgreSQL Source Code  git master
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 
35 static Datum X509_NAME_field_to_text(X509_NAME *name, text *fieldName);
36 static Datum ASN1_STRING_to_text(ASN1_STRING *str);
37 
38 /*
39  * Function context for data persisting over repeated calls.
40  */
41 typedef 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  */
53 Datum
55 {
57 }
58 
59 
60 /*
61  * Returns SSL version currently in use.
62  */
64 Datum
66 {
67  const char *version;
68 
69  if (!MyProcPort->ssl_in_use)
71 
72  version = be_tls_get_version(MyProcPort);
73  if (version == NULL)
75 
77 }
78 
79 
80 /*
81  * Returns SSL cipher currently in use.
82  */
84 Datum
86 {
87  const char *cipher;
88 
89  if (!MyProcPort->ssl_in_use)
91 
92  cipher = be_tls_get_cipher(MyProcPort);
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  */
107 Datum
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  */
123 Datum
125 {
126  char decimal[NAMEDATALEN];
127  Datum result;
128 
130  PG_RETURN_NULL();
131 
133 
134  if (!*decimal)
135  PG_RETURN_NULL();
136 
139  ObjectIdGetDatum(0),
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  */
158 static 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)
170  ereport(ERROR,
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  */
204 static Datum
205 X509_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)
215  ereport(ERROR,
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));
224  return ASN1_STRING_to_text(data);
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  */
245 Datum
247 {
248  text *fieldname = PG_GETARG_TEXT_PP(0);
249  Datum result;
250 
252  PG_RETURN_NULL();
253 
254  result = X509_NAME_field_to_text(X509_get_subject_name(MyProcPort->peer), fieldname);
255 
256  if (!result)
257  PG_RETURN_NULL();
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  */
280 Datum
282 {
283  text *fieldname = PG_GETARG_TEXT_PP(0);
284  Datum result;
285 
286  if (!(MyProcPort->peer))
287  PG_RETURN_NULL();
288 
289  result = X509_NAME_field_to_text(X509_get_issuer_name(MyProcPort->peer), fieldname);
290 
291  if (!result)
292  PG_RETURN_NULL();
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  */
308 Datum
310 {
311  char subject[NAMEDATALEN];
312 
314  PG_RETURN_NULL();
315 
317 
318  if (!*subject)
319  PG_RETURN_NULL();
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  */
335 Datum
337 {
338  char issuer[NAMEDATALEN];
339 
341  PG_RETURN_NULL();
342 
344 
345  if (!*issuer)
346  PG_RETURN_NULL();
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  */
361 Datum
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)
389  ereport(ERROR,
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)
440  ereport(ERROR,
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)
451  ereport(ERROR,
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)
460  ereport(ERROR,
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:626
const char * be_tls_get_version(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)
const char * be_tls_get_cipher(Port *port)
void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
static Datum values[MAXATTR]
Definition: bootstrap.c:152
#define CStringGetTextDatum(s)
Definition: builtins.h:97
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
#define ereport(elevel,...)
Definition: elog.h:149
TupleDesc BlessTupleDesc(TupleDesc tupdesc)
Definition: execTuples.c:2070
#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:646
#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:49
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition: heaptuple.c:1116
char * pg_any_to_server(const char *s, int len, int encoding)
Definition: mbutils.c:676
void pfree(void *pointer)
Definition: mcxt.c:1508
void * palloc(Size size)
Definition: mcxt.c:1304
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:73
@ PG_UTF8
Definition: pg_wchar.h:232
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
uintptr_t Datum
Definition: postgres.h:64
static Datum BoolGetDatum(bool X)
Definition: postgres.h:102
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:350
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:212
static pg_noinline void Size size
Definition: slab.c:607
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:202
bool peer_cert_valid
Definition: libpq-be.h:205
Definition: type.h:95
Definition: c.h:674
char * text_to_cstring(const text *t)
Definition: varlena.c:217
text * cstring_to_text_with_len(const char *s, int len)
Definition: varlena.c:196
text * cstring_to_text(const char *s)
Definition: varlena.c:184
const char * name