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 "utils/numeric.h"
13 #include "libpq/libpq-be.h"
14 #include "miscadmin.h"
15 #include "utils/builtins.h"
16 #include "mb/pg_wchar.h"
18 #include <openssl/x509.h>
19 #include <openssl/asn1.h>
25 Datum ssl_is_used(PG_FUNCTION_ARGS);
26 Datum ssl_version(PG_FUNCTION_ARGS);
27 Datum ssl_cipher(PG_FUNCTION_ARGS);
28 Datum ssl_client_cert_present(PG_FUNCTION_ARGS);
29 Datum ssl_client_serial(PG_FUNCTION_ARGS);
30 Datum ssl_client_dn_field(PG_FUNCTION_ARGS);
31 Datum ssl_issuer_field(PG_FUNCTION_ARGS);
32 Datum ssl_client_dn(PG_FUNCTION_ARGS);
33 Datum ssl_issuer_dn(PG_FUNCTION_ARGS);
34 Datum X509_NAME_field_to_text(X509_NAME *name, text *fieldName);
35 Datum X509_NAME_to_text(X509_NAME *name);
36 Datum ASN1_STRING_to_text(ASN1_STRING *str);
40 * Indicates whether current session uses SSL
42 * Function has no arguments. Returns bool. True if current session
43 * is SSL session and false if it is local or non-ssl session.
45 PG_FUNCTION_INFO_V1(ssl_is_used);
47 ssl_is_used(PG_FUNCTION_ARGS)
49 PG_RETURN_BOOL(MyProcPort->ssl != NULL);
54 * Returns SSL cipher currently in use.
56 PG_FUNCTION_INFO_V1(ssl_version);
58 ssl_version(PG_FUNCTION_ARGS)
60 if (MyProcPort->ssl == NULL)
62 PG_RETURN_TEXT_P(cstring_to_text(SSL_get_version(MyProcPort->ssl)));
67 * Returns SSL cipher currently in use.
69 PG_FUNCTION_INFO_V1(ssl_cipher);
71 ssl_cipher(PG_FUNCTION_ARGS)
73 if (MyProcPort->ssl == NULL)
75 PG_RETURN_TEXT_P(cstring_to_text(SSL_get_cipher(MyProcPort->ssl)));
80 * Indicates whether current client have provided a certificate
82 * Function has no arguments. Returns bool. True if current session
83 * is SSL session and client certificate is verified, otherwise false.
85 PG_FUNCTION_INFO_V1(ssl_client_cert_present);
87 ssl_client_cert_present(PG_FUNCTION_ARGS)
89 PG_RETURN_BOOL(MyProcPort->peer != NULL);
94 * Returns serial number of certificate used to establish current
97 * Function has no arguments. It returns the certificate serial
98 * number as numeric or null if current session doesn't use SSL or if
99 * SSL connection is established without sending client certificate.
101 PG_FUNCTION_INFO_V1(ssl_client_serial);
103 ssl_client_serial(PG_FUNCTION_ARGS)
106 Port *port = MyProcPort;
107 X509 *peer = port->peer;
108 ASN1_INTEGER *serial = NULL;
114 serial = X509_get_serialNumber(peer);
115 b = ASN1_INTEGER_to_BN(serial, NULL);
116 decimal = BN_bn2dec(b);
119 result = DirectFunctionCall3(numeric_in,
120 CStringGetDatum(decimal),
123 OPENSSL_free(decimal);
129 * Converts OpenSSL ASN1_STRING structure into text
131 * Converts ASN1_STRING into text, converting all the characters into
132 * current database encoding if possible. Any invalid characters are
133 * replaced by question marks.
135 * Parameter: str - OpenSSL ASN1_STRING structure. Memory managment
136 * of this structure is responsibility of caller.
138 * Returns Datum, which can be directly returned from a C language SQL
142 ASN1_STRING_to_text(ASN1_STRING *str)
151 membuf = BIO_new(BIO_s_mem());
152 (void) BIO_set_close(membuf, BIO_CLOSE);
153 ASN1_STRING_print_ex(membuf, str,
154 ((ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB)
155 | ASN1_STRFLGS_UTF8_CONVERT));
156 /* ensure null termination of the BIO's content */
158 BIO_write(membuf, &nullterm, 1);
159 size = BIO_get_mem_data(membuf, &sp);
160 dp = (char *) pg_do_encoding_conversion((unsigned char *) sp,
163 GetDatabaseEncoding());
164 result = cstring_to_text(dp);
169 PG_RETURN_TEXT_P(result);
174 * Returns specified field of specified X509_NAME structure
176 * Common part of ssl_client_dn and ssl_issuer_dn functions.
178 * Parameter: X509_NAME *name - either subject or issuer of certificate
179 * Parameter: text fieldName - field name string like 'CN' or commonName
180 * to be looked up in the OpenSSL ASN1 OID database
182 * Returns result of ASN1_STRING_to_text applied to appropriate
186 X509_NAME_field_to_text(X509_NAME *name, text *fieldName)
188 char *string_fieldname;
193 string_fieldname = text_to_cstring(fieldName);
194 nid = OBJ_txt2nid(string_fieldname);
195 if (nid == NID_undef)
197 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
198 errmsg("invalid X.509 field name: \"%s\"",
200 pfree(string_fieldname);
201 index = X509_NAME_get_index_by_NID(name, nid, -1);
204 data = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, index));
205 return ASN1_STRING_to_text(data);
210 * Returns specified field of client certificate distinguished name
212 * Receives field name (like 'commonName' and 'emailAddress') and
213 * returns appropriate part of certificate subject converted into
216 * Parameter: fieldname text - will be looked up in OpenSSL object
217 * identifier database
219 * Returns text string with appropriate value.
221 * Throws an error if argument cannot be converted into ASN1 OID by
222 * OpenSSL. Returns null if no client certificate is present, or if
223 * there is no field with such name in the certificate.
225 PG_FUNCTION_INFO_V1(ssl_client_dn_field);
227 ssl_client_dn_field(PG_FUNCTION_ARGS)
229 text *fieldname = PG_GETARG_TEXT_P(0);
232 if (!(MyProcPort->peer))
235 result = X509_NAME_field_to_text(X509_get_subject_name(MyProcPort->peer), fieldname);
245 * Returns specified field of client certificate issuer name
247 * Receives field name (like 'commonName' and 'emailAddress') and
248 * returns appropriate part of certificate subject converted into
251 * Parameter: fieldname text - would be looked up in OpenSSL object
252 * identifier database
254 * Returns text string with appropriate value.
256 * Throws an error if argument cannot be converted into ASN1 OID by
257 * OpenSSL. Returns null if no client certificate is present, or if
258 * there is no field with such name in the certificate.
260 PG_FUNCTION_INFO_V1(ssl_issuer_field);
262 ssl_issuer_field(PG_FUNCTION_ARGS)
264 text *fieldname = PG_GETARG_TEXT_P(0);
267 if (!(MyProcPort->peer))
270 result = X509_NAME_field_to_text(X509_get_issuer_name(MyProcPort->peer), fieldname);
280 * Equivalent of X509_NAME_oneline that respects encoding
282 * This function converts X509_NAME structure to the text variable
283 * converting all textual data into current database encoding.
285 * Parameter: X509_NAME *name X509_NAME structure to be converted
287 * Returns: text datum which contains string representation of
291 X509_NAME_to_text(X509_NAME *name)
293 BIO *membuf = BIO_new(BIO_s_mem());
296 count = X509_NAME_entry_count(name);
299 const char *field_name;
306 (void) BIO_set_close(membuf, BIO_CLOSE);
307 for (i = 0; i < count; i++)
309 e = X509_NAME_get_entry(name, i);
310 nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(e));
311 v = X509_NAME_ENTRY_get_data(e);
312 field_name = OBJ_nid2sn(nid);
314 field_name = OBJ_nid2ln(nid);
315 BIO_printf(membuf, "/%s=", field_name);
316 ASN1_STRING_print_ex(membuf, v,
317 ((ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB)
318 | ASN1_STRFLGS_UTF8_CONVERT));
321 /* ensure null termination of the BIO's content */
323 BIO_write(membuf, &nullterm, 1);
324 size = BIO_get_mem_data(membuf, &sp);
325 dp = (char *) pg_do_encoding_conversion((unsigned char *) sp,
328 GetDatabaseEncoding());
329 result = cstring_to_text(dp);
334 PG_RETURN_TEXT_P(result);
339 * Returns current client certificate subject as one string
341 * This function returns distinguished name (subject) of the client
342 * certificate used in the current SSL connection, converting it into
343 * the current database encoding.
345 * Returns text datum.
347 PG_FUNCTION_INFO_V1(ssl_client_dn);
349 ssl_client_dn(PG_FUNCTION_ARGS)
351 if (!(MyProcPort->peer))
353 return X509_NAME_to_text(X509_get_subject_name(MyProcPort->peer));
358 * Returns current client certificate issuer as one string
360 * This function returns issuer's distinguished name of the client
361 * certificate used in the current SSL connection, converting it into
362 * the current database encoding.
364 * Returns text datum.
366 PG_FUNCTION_INFO_V1(ssl_issuer_dn);
368 ssl_issuer_dn(PG_FUNCTION_ARGS)
370 if (!(MyProcPort->peer))
372 return X509_NAME_to_text(X509_get_issuer_name(MyProcPort->peer));