]> granicus.if.org Git - postgresql/blob - contrib/sslinfo/sslinfo.c
Remove cvs keywords from all files.
[postgresql] / contrib / sslinfo / sslinfo.c
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 #include "fmgr.h"
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"
17
18 #include <openssl/x509.h>
19 #include <openssl/asn1.h>
20
21
22 PG_MODULE_MAGIC;
23
24
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);
37
38
39 /*
40  * Indicates whether current session uses SSL
41  *
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.
44  */
45 PG_FUNCTION_INFO_V1(ssl_is_used);
46 Datum
47 ssl_is_used(PG_FUNCTION_ARGS)
48 {
49         PG_RETURN_BOOL(MyProcPort->ssl != NULL);
50 }
51
52
53 /*
54  * Returns SSL cipher currently in use.
55  */
56 PG_FUNCTION_INFO_V1(ssl_version);
57 Datum
58 ssl_version(PG_FUNCTION_ARGS)
59 {
60         if (MyProcPort->ssl == NULL)
61                 PG_RETURN_NULL();
62         PG_RETURN_TEXT_P(cstring_to_text(SSL_get_version(MyProcPort->ssl)));
63 }
64
65
66 /*
67  * Returns SSL cipher currently in use.
68  */
69 PG_FUNCTION_INFO_V1(ssl_cipher);
70 Datum
71 ssl_cipher(PG_FUNCTION_ARGS)
72 {
73         if (MyProcPort->ssl == NULL)
74                 PG_RETURN_NULL();
75         PG_RETURN_TEXT_P(cstring_to_text(SSL_get_cipher(MyProcPort->ssl)));
76 }
77
78
79 /*
80  * Indicates whether current client have provided a certificate
81  *
82  * Function has no arguments.  Returns bool.  True if current session
83  * is SSL session and client certificate is verified, otherwise false.
84  */
85 PG_FUNCTION_INFO_V1(ssl_client_cert_present);
86 Datum
87 ssl_client_cert_present(PG_FUNCTION_ARGS)
88 {
89         PG_RETURN_BOOL(MyProcPort->peer != NULL);
90 }
91
92
93 /*
94  * Returns serial number of certificate used to establish current
95  * session
96  *
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.
100  */
101 PG_FUNCTION_INFO_V1(ssl_client_serial);
102 Datum
103 ssl_client_serial(PG_FUNCTION_ARGS)
104 {
105         Datum           result;
106         Port       *port = MyProcPort;
107         X509       *peer = port->peer;
108         ASN1_INTEGER *serial = NULL;
109         BIGNUM     *b;
110         char       *decimal;
111
112         if (!peer)
113                 PG_RETURN_NULL();
114         serial = X509_get_serialNumber(peer);
115         b = ASN1_INTEGER_to_BN(serial, NULL);
116         decimal = BN_bn2dec(b);
117
118         BN_free(b);
119         result = DirectFunctionCall3(numeric_in,
120                                                                  CStringGetDatum(decimal),
121                                                                  ObjectIdGetDatum(0),
122                                                                  Int32GetDatum(-1));
123         OPENSSL_free(decimal);
124         return result;
125 }
126
127
128 /*
129  * Converts OpenSSL ASN1_STRING structure into text
130  *
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.
134  *
135  * Parameter: str - OpenSSL ASN1_STRING structure.      Memory managment
136  * of this structure is responsibility of caller.
137  *
138  * Returns Datum, which can be directly returned from a C language SQL
139  * function.
140  */
141 Datum
142 ASN1_STRING_to_text(ASN1_STRING *str)
143 {
144         BIO                *membuf;
145         size_t          size;
146         char            nullterm;
147         char       *sp;
148         char       *dp;
149         text       *result;
150
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 */
157         nullterm = '\0';
158         BIO_write(membuf, &nullterm, 1);
159         size = BIO_get_mem_data(membuf, &sp);
160         dp = (char *) pg_do_encoding_conversion((unsigned char *) sp,
161                                                                                         size - 1,
162                                                                                         PG_UTF8,
163                                                                                         GetDatabaseEncoding());
164         result = cstring_to_text(dp);
165         if (dp != sp)
166                 pfree(dp);
167         BIO_free(membuf);
168
169         PG_RETURN_TEXT_P(result);
170 }
171
172
173 /*
174  * Returns specified field of specified X509_NAME structure
175  *
176  * Common part of ssl_client_dn and ssl_issuer_dn functions.
177  *
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
181  *
182  * Returns result of ASN1_STRING_to_text applied to appropriate
183  * part of name
184  */
185 Datum
186 X509_NAME_field_to_text(X509_NAME *name, text *fieldName)
187 {
188         char       *string_fieldname;
189         int                     nid,
190                                 index;
191         ASN1_STRING *data;
192
193         string_fieldname = text_to_cstring(fieldName);
194         nid = OBJ_txt2nid(string_fieldname);
195         if (nid == NID_undef)
196                 ereport(ERROR,
197                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
198                                  errmsg("invalid X.509 field name: \"%s\"",
199                                                 string_fieldname)));
200         pfree(string_fieldname);
201         index = X509_NAME_get_index_by_NID(name, nid, -1);
202         if (index < 0)
203                 return (Datum) 0;
204         data = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, index));
205         return ASN1_STRING_to_text(data);
206 }
207
208
209 /*
210  * Returns specified field of client certificate distinguished name
211  *
212  * Receives field name (like 'commonName' and 'emailAddress') and
213  * returns appropriate part of certificate subject converted into
214  * database encoding.
215  *
216  * Parameter: fieldname text - will be looked up in OpenSSL object
217  * identifier database
218  *
219  * Returns text string with appropriate value.
220  *
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.
224  */
225 PG_FUNCTION_INFO_V1(ssl_client_dn_field);
226 Datum
227 ssl_client_dn_field(PG_FUNCTION_ARGS)
228 {
229         text       *fieldname = PG_GETARG_TEXT_P(0);
230         Datum           result;
231
232         if (!(MyProcPort->peer))
233                 PG_RETURN_NULL();
234
235         result = X509_NAME_field_to_text(X509_get_subject_name(MyProcPort->peer), fieldname);
236
237         if (!result)
238                 PG_RETURN_NULL();
239         else
240                 return result;
241 }
242
243
244 /*
245  * Returns specified field of client certificate issuer name
246  *
247  * Receives field name (like 'commonName' and 'emailAddress') and
248  * returns appropriate part of certificate subject converted into
249  * database encoding.
250  *
251  * Parameter: fieldname text - would be looked up in OpenSSL object
252  * identifier database
253  *
254  * Returns text string with appropriate value.
255  *
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.
259  */
260 PG_FUNCTION_INFO_V1(ssl_issuer_field);
261 Datum
262 ssl_issuer_field(PG_FUNCTION_ARGS)
263 {
264         text       *fieldname = PG_GETARG_TEXT_P(0);
265         Datum           result;
266
267         if (!(MyProcPort->peer))
268                 PG_RETURN_NULL();
269
270         result = X509_NAME_field_to_text(X509_get_issuer_name(MyProcPort->peer), fieldname);
271
272         if (!result)
273                 PG_RETURN_NULL();
274         else
275                 return result;
276 }
277
278
279 /*
280  * Equivalent of X509_NAME_oneline that respects encoding
281  *
282  * This function converts X509_NAME structure to the text variable
283  * converting all textual data into current database encoding.
284  *
285  * Parameter: X509_NAME *name X509_NAME structure to be converted
286  *
287  * Returns: text datum which contains string representation of
288  * X509_NAME
289  */
290 Datum
291 X509_NAME_to_text(X509_NAME *name)
292 {
293         BIO                *membuf = BIO_new(BIO_s_mem());
294         int                     i,
295                                 nid,
296                                 count = X509_NAME_entry_count(name);
297         X509_NAME_ENTRY *e;
298         ASN1_STRING *v;
299         const char *field_name;
300         size_t          size;
301         char            nullterm;
302         char       *sp;
303         char       *dp;
304         text       *result;
305
306         (void) BIO_set_close(membuf, BIO_CLOSE);
307         for (i = 0; i < count; i++)
308         {
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);
313                 if (!field_name)
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));
319         }
320
321         /* ensure null termination of the BIO's content */
322         nullterm = '\0';
323         BIO_write(membuf, &nullterm, 1);
324         size = BIO_get_mem_data(membuf, &sp);
325         dp = (char *) pg_do_encoding_conversion((unsigned char *) sp,
326                                                                                         size - 1,
327                                                                                         PG_UTF8,
328                                                                                         GetDatabaseEncoding());
329         result = cstring_to_text(dp);
330         if (dp != sp)
331                 pfree(dp);
332         BIO_free(membuf);
333
334         PG_RETURN_TEXT_P(result);
335 }
336
337
338 /*
339  * Returns current client certificate subject as one string
340  *
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.
344  *
345  * Returns text datum.
346  */
347 PG_FUNCTION_INFO_V1(ssl_client_dn);
348 Datum
349 ssl_client_dn(PG_FUNCTION_ARGS)
350 {
351         if (!(MyProcPort->peer))
352                 PG_RETURN_NULL();
353         return X509_NAME_to_text(X509_get_subject_name(MyProcPort->peer));
354 }
355
356
357 /*
358  * Returns current client certificate issuer as one string
359  *
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.
363  *
364  * Returns text datum.
365  */
366 PG_FUNCTION_INFO_V1(ssl_issuer_dn);
367 Datum
368 ssl_issuer_dn(PG_FUNCTION_ARGS)
369 {
370         if (!(MyProcPort->peer))
371                 PG_RETURN_NULL();
372         return X509_NAME_to_text(X509_get_issuer_name(MyProcPort->peer));
373 }