Header And Logo

PostgreSQL
| The world's most advanced open source database.

 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
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 #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 
23 
24 
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  */
46 Datum
48 {
50 }
51 
52 
53 /*
54  * Returns SSL cipher currently in use.
55  */
57 Datum
59 {
60  if (MyProcPort->ssl == 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  */
70 Datum
72 {
73  if (MyProcPort->ssl == 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  */
86 Datum
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  */
102 Datum
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);
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 management
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,
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  */
226 Datum
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  */
261 Datum
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
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,
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  */
348 Datum
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  */
367 Datum
369 {
370  if (!(MyProcPort->peer))
371  PG_RETURN_NULL();
372  return X509_NAME_to_text(X509_get_issuer_name(MyProcPort->peer));
373 }