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 
23 
24 static Datum X509_NAME_field_to_text(X509_NAME *name, text *fieldName);
25 static Datum ASN1_STRING_to_text(ASN1_STRING *str);
26 
27 /*
28  * Function context for data persisting over repeated calls.
29  */
30 typedef struct
31 {
34 
35 /*
36  * Indicates whether current session uses SSL
37  *
38  * Function has no arguments. Returns bool. True if current session
39  * is SSL session and false if it is local or non-ssl session.
40  */
42 Datum
44 {
46 }
47 
48 
49 /*
50  * Returns SSL version currently in use.
51  */
53 Datum
55 {
56  const char *version;
57 
58  if (!MyProcPort->ssl_in_use)
60 
61  version = be_tls_get_version(MyProcPort);
62  if (version == NULL)
64 
66 }
67 
68 
69 /*
70  * Returns SSL cipher currently in use.
71  */
73 Datum
75 {
76  const char *cipher;
77 
78  if (!MyProcPort->ssl_in_use)
80 
81  cipher = be_tls_get_cipher(MyProcPort);
82  if (cipher == NULL)
84 
86 }
87 
88 
89 /*
90  * Indicates whether current client provided a certificate
91  *
92  * Function has no arguments. Returns bool. True if current session
93  * is SSL session and client certificate is verified, otherwise false.
94  */
96 Datum
98 {
100 }
101 
102 
103 /*
104  * Returns serial number of certificate used to establish current
105  * session
106  *
107  * Function has no arguments. It returns the certificate serial
108  * number as numeric or null if current session doesn't use SSL or if
109  * SSL connection is established without sending client certificate.
110  */
112 Datum
114 {
115  char decimal[NAMEDATALEN];
116  Datum result;
117 
119  PG_RETURN_NULL();
120 
122 
123  if (!*decimal)
124  PG_RETURN_NULL();
125 
127  CStringGetDatum(decimal),
128  ObjectIdGetDatum(0),
129  Int32GetDatum(-1));
130  return result;
131 }
132 
133 
134 /*
135  * Converts OpenSSL ASN1_STRING structure into text
136  *
137  * Converts ASN1_STRING into text, converting all the characters into
138  * current database encoding if possible. Any invalid characters are
139  * replaced by question marks.
140  *
141  * Parameter: str - OpenSSL ASN1_STRING structure. Memory management
142  * of this structure is responsibility of caller.
143  *
144  * Returns Datum, which can be directly returned from a C language SQL
145  * function.
146  */
147 static Datum
149 {
150  BIO *membuf;
151  size_t size;
152  char nullterm;
153  char *sp;
154  char *dp;
155  text *result;
156 
157  membuf = BIO_new(BIO_s_mem());
158  if (membuf == NULL)
159  ereport(ERROR,
160  (errcode(ERRCODE_OUT_OF_MEMORY),
161  errmsg("could not create OpenSSL BIO structure")));
162  (void) BIO_set_close(membuf, BIO_CLOSE);
163  ASN1_STRING_print_ex(membuf, str,
164  ((ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB)
165  | ASN1_STRFLGS_UTF8_CONVERT));
166  /* ensure null termination of the BIO's content */
167  nullterm = '\0';
168  BIO_write(membuf, &nullterm, 1);
169  size = BIO_get_mem_data(membuf, &sp);
170  dp = pg_any_to_server(sp, size - 1, PG_UTF8);
171  result = cstring_to_text(dp);
172  if (dp != sp)
173  pfree(dp);
174  if (BIO_free(membuf) != 1)
175  elog(ERROR, "could not free OpenSSL BIO structure");
176 
177  PG_RETURN_TEXT_P(result);
178 }
179 
180 
181 /*
182  * Returns specified field of specified X509_NAME structure
183  *
184  * Common part of ssl_client_dn and ssl_issuer_dn functions.
185  *
186  * Parameter: X509_NAME *name - either subject or issuer of certificate
187  * Parameter: text fieldName - field name string like 'CN' or commonName
188  * to be looked up in the OpenSSL ASN1 OID database
189  *
190  * Returns result of ASN1_STRING_to_text applied to appropriate
191  * part of name
192  */
193 static Datum
194 X509_NAME_field_to_text(X509_NAME *name, text *fieldName)
195 {
196  char *string_fieldname;
197  int nid,
198  index;
199  ASN1_STRING *data;
200 
201  string_fieldname = text_to_cstring(fieldName);
202  nid = OBJ_txt2nid(string_fieldname);
203  if (nid == NID_undef)
204  ereport(ERROR,
205  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
206  errmsg("invalid X.509 field name: \"%s\"",
207  string_fieldname)));
208  pfree(string_fieldname);
209  index = X509_NAME_get_index_by_NID(name, nid, -1);
210  if (index < 0)
211  return (Datum) 0;
212  data = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, index));
213  return ASN1_STRING_to_text(data);
214 }
215 
216 
217 /*
218  * Returns specified field of client certificate distinguished name
219  *
220  * Receives field name (like 'commonName' and 'emailAddress') and
221  * returns appropriate part of certificate subject converted into
222  * database encoding.
223  *
224  * Parameter: fieldname text - will be looked up in OpenSSL object
225  * identifier database
226  *
227  * Returns text string with appropriate value.
228  *
229  * Throws an error if argument cannot be converted into ASN1 OID by
230  * OpenSSL. Returns null if no client certificate is present, or if
231  * there is no field with such name in the certificate.
232  */
234 Datum
236 {
237  text *fieldname = PG_GETARG_TEXT_PP(0);
238  Datum result;
239 
241  PG_RETURN_NULL();
242 
243  result = X509_NAME_field_to_text(X509_get_subject_name(MyProcPort->peer), fieldname);
244 
245  if (!result)
246  PG_RETURN_NULL();
247  else
248  return result;
249 }
250 
251 
252 /*
253  * Returns specified field of client certificate issuer name
254  *
255  * Receives field name (like 'commonName' and 'emailAddress') and
256  * returns appropriate part of certificate subject converted into
257  * database encoding.
258  *
259  * Parameter: fieldname text - would be looked up in OpenSSL object
260  * identifier database
261  *
262  * Returns text string with appropriate value.
263  *
264  * Throws an error if argument cannot be converted into ASN1 OID by
265  * OpenSSL. Returns null if no client certificate is present, or if
266  * there is no field with such name in the certificate.
267  */
269 Datum
271 {
272  text *fieldname = PG_GETARG_TEXT_PP(0);
273  Datum result;
274 
275  if (!(MyProcPort->peer))
276  PG_RETURN_NULL();
277 
278  result = X509_NAME_field_to_text(X509_get_issuer_name(MyProcPort->peer), fieldname);
279 
280  if (!result)
281  PG_RETURN_NULL();
282  else
283  return result;
284 }
285 
286 
287 /*
288  * Returns current client certificate subject as one string
289  *
290  * This function returns distinguished name (subject) of the client
291  * certificate used in the current SSL connection, converting it into
292  * the current database encoding.
293  *
294  * Returns text datum.
295  */
297 Datum
299 {
300  char subject[NAMEDATALEN];
301 
303  PG_RETURN_NULL();
304 
306 
307  if (!*subject)
308  PG_RETURN_NULL();
309 
311 }
312 
313 
314 /*
315  * Returns current client certificate issuer as one string
316  *
317  * This function returns issuer's distinguished name of the client
318  * certificate used in the current SSL connection, converting it into
319  * the current database encoding.
320  *
321  * Returns text datum.
322  */
324 Datum
326 {
327  char issuer[NAMEDATALEN];
328 
330  PG_RETURN_NULL();
331 
333 
334  if (!*issuer)
335  PG_RETURN_NULL();
336 
338 }
339 
340 
341 /*
342  * Returns information about available SSL extensions.
343  *
344  * Returns setof record made of the following values:
345  * - name of the extension.
346  * - value of the extension.
347  * - critical status of the extension.
348  */
350 Datum
352 {
353  X509 *cert = MyProcPort->peer;
354  FuncCallContext *funcctx;
355  int call_cntr;
356  int max_calls;
357  MemoryContext oldcontext;
359 
360  if (SRF_IS_FIRSTCALL())
361  {
362 
363  TupleDesc tupdesc;
364 
365  /* create a function context for cross-call persistence */
366  funcctx = SRF_FIRSTCALL_INIT();
367 
368  /*
369  * Switch to memory context appropriate for multiple function calls
370  */
371  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
372 
373  /* Create a user function context for cross-call persistence */
375 
376  /* Construct tuple descriptor */
377  if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
378  ereport(ERROR,
379  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
380  errmsg("function returning record called in context that cannot accept type record")));
381  fctx->tupdesc = BlessTupleDesc(tupdesc);
382 
383  /* Set max_calls as a count of extensions in certificate */
384  max_calls = cert != NULL ? X509_get_ext_count(cert) : 0;
385 
386  if (max_calls > 0)
387  {
388  /* got results, keep track of them */
389  funcctx->max_calls = max_calls;
390  funcctx->user_fctx = fctx;
391  }
392  else
393  {
394  /* fast track when no results */
395  MemoryContextSwitchTo(oldcontext);
396  SRF_RETURN_DONE(funcctx);
397  }
398 
399  MemoryContextSwitchTo(oldcontext);
400  }
401 
402  /* stuff done on every call of the function */
403  funcctx = SRF_PERCALL_SETUP();
404 
405  /*
406  * Initialize per-call variables.
407  */
408  call_cntr = funcctx->call_cntr;
409  max_calls = funcctx->max_calls;
410  fctx = funcctx->user_fctx;
411 
412  /* do while there are more left to send */
413  if (call_cntr < max_calls)
414  {
415  Datum values[3];
416  bool nulls[3];
417  char *buf;
418  HeapTuple tuple;
419  Datum result;
420  BIO *membuf;
421  X509_EXTENSION *ext;
422  ASN1_OBJECT *obj;
423  int nid;
424  int len;
425 
426  /* need a BIO for this */
427  membuf = BIO_new(BIO_s_mem());
428  if (membuf == NULL)
429  ereport(ERROR,
430  (errcode(ERRCODE_OUT_OF_MEMORY),
431  errmsg("could not create OpenSSL BIO structure")));
432 
433  /* Get the extension from the certificate */
434  ext = X509_get_ext(cert, call_cntr);
435  obj = X509_EXTENSION_get_object(ext);
436 
437  /* Get the extension name */
438  nid = OBJ_obj2nid(obj);
439  if (nid == NID_undef)
440  ereport(ERROR,
441  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
442  errmsg("unknown OpenSSL extension in certificate at position %d",
443  call_cntr)));
444  values[0] = CStringGetTextDatum(OBJ_nid2sn(nid));
445  nulls[0] = false;
446 
447  /* Get the extension value */
448  if (X509V3_EXT_print(membuf, ext, 0, 0) <= 0)
449  ereport(ERROR,
450  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
451  errmsg("could not print extension value in certificate at position %d",
452  call_cntr)));
453  len = BIO_get_mem_data(membuf, &buf);
454  values[1] = PointerGetDatum(cstring_to_text_with_len(buf, len));
455  nulls[1] = false;
456 
457  /* Get critical status */
458  values[2] = BoolGetDatum(X509_EXTENSION_get_critical(ext));
459  nulls[2] = false;
460 
461  /* Build tuple */
462  tuple = heap_form_tuple(fctx->tupdesc, values, nulls);
463  result = HeapTupleGetDatum(tuple);
464 
465  if (BIO_free(membuf) != 1)
466  elog(ERROR, "could not free OpenSSL BIO structure");
467 
468  SRF_RETURN_NEXT(funcctx, result);
469  }
470 
471  /* All done */
472  SRF_RETURN_DONE(funcctx);
473 }
uint64 call_cntr
Definition: funcapi.h:65
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition: funcapi.c:207
struct Port * MyProcPort
Definition: globals.c:46
static Datum X509_NAME_field_to_text(X509_NAME *name, text *fieldName)
Definition: sslinfo.c:194
#define SRF_IS_FIRSTCALL()
Definition: funcapi.h:293
#define PointerGetDatum(X)
Definition: postgres.h:600
bool peer_cert_valid
Definition: libpq-be.h:212
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
int errcode(int sqlerrcode)
Definition: elog.c:698
bool ssl_in_use
Definition: libpq-be.h:209
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
Datum ssl_client_serial(PG_FUNCTION_ARGS)
Definition: sslinfo.c:113
#define SRF_PERCALL_SETUP()
Definition: funcapi.h:297
Datum ssl_extension_info(PG_FUNCTION_ARGS)
Definition: sslinfo.c:351
#define PG_GETARG_TEXT_PP(n)
Definition: fmgr.h:309
Definition: type.h:89
#define NAMEDATALEN
#define SRF_RETURN_NEXT(_funcctx, _result)
Definition: funcapi.h:299
Datum ssl_is_used(PG_FUNCTION_ARGS)
Definition: sslinfo.c:43
static Datum ASN1_STRING_to_text(ASN1_STRING *str)
Definition: sslinfo.c:148
void pfree(void *pointer)
Definition: mcxt.c:1169
#define ObjectIdGetDatum(X)
Definition: postgres.h:551
#define ERROR
Definition: elog.h:46
Datum ssl_client_cert_present(PG_FUNCTION_ARGS)
Definition: sslinfo.c:97
Datum ssl_client_dn_field(PG_FUNCTION_ARGS)
Definition: sslinfo.c:235
TupleDesc BlessTupleDesc(TupleDesc tupdesc)
Definition: execTuples.c:2082
static char * buf
Definition: pg_test_fsync.c:68
text * cstring_to_text_with_len(const char *s, int len)
Definition: varlena.c:202
#define CStringGetDatum(X)
Definition: postgres.h:622
Datum numeric_in(PG_FUNCTION_ARGS)
Definition: numeric.c:625
#define DirectFunctionCall3(func, arg1, arg2, arg3)
Definition: fmgr.h:630
Datum ssl_issuer_dn(PG_FUNCTION_ARGS)
Definition: sslinfo.c:325
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359
uintptr_t Datum
Definition: postgres.h:411
#define BoolGetDatum(X)
Definition: postgres.h:446
Datum ssl_version(PG_FUNCTION_ARGS)
Definition: sslinfo.c:54
#define ereport(elevel,...)
Definition: elog.h:157
Datum ssl_client_dn(PG_FUNCTION_ARGS)
Definition: sslinfo.c:298
#define PG_RETURN_TEXT_P(x)
Definition: fmgr.h:372
text * cstring_to_text(const char *s)
Definition: varlena.c:190
Datum ssl_issuer_field(PG_FUNCTION_ARGS)
Definition: sslinfo.c:270
MemoryContext multi_call_memory_ctx
Definition: funcapi.h:101
#define HeapTupleGetDatum(tuple)
Definition: funcapi.h:220
PG_MODULE_MAGIC
Definition: sslinfo.c:22
const char * name
Definition: encode.c:515
const char * be_tls_get_version(Port *port)
void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len)
static Datum values[MAXATTR]
Definition: bootstrap.c:166
char * text_to_cstring(const text *t)
Definition: varlena.c:223
#define Int32GetDatum(X)
Definition: postgres.h:523
Datum ssl_cipher(PG_FUNCTION_ARGS)
Definition: sslinfo.c:74
void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
void * user_fctx
Definition: funcapi.h:82
void * palloc(Size size)
Definition: mcxt.c:1062
int errmsg(const char *fmt,...)
Definition: elog.c:909
void be_tls_get_peer_serial(Port *port, char *ptr, size_t len)
#define elog(elevel,...)
Definition: elog.h:232
#define CStringGetTextDatum(s)
Definition: builtins.h:82
Definition: c.h:621
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
const char * be_tls_get_cipher(Port *port)
PG_FUNCTION_INFO_V1(ssl_is_used)
char * pg_any_to_server(const char *s, int len, int encoding)
Definition: mbutils.c:676
#define PG_RETURN_NULL()
Definition: fmgr.h:345
uint64 max_calls
Definition: funcapi.h:74
#define SRF_RETURN_DONE(_funcctx)
Definition: funcapi.h:317
#define SRF_FIRSTCALL_INIT()
Definition: funcapi.h:295