PostgreSQL Source Code: contrib/sslinfo/sslinfo.c Source File

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

AltStyle によって変換されたページ (->オリジナル) /