PostgreSQL Source Code  git master
 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 
22 
23 static Datum X509_NAME_field_to_text(X509_NAME *name, text *fieldName);
24 static Datum X509_NAME_to_text(X509_NAME *name);
25 static Datum ASN1_STRING_to_text(ASN1_STRING *str);
26 
27 
28 /*
29  * Indicates whether current session uses SSL
30  *
31  * Function has no arguments. Returns bool. True if current session
32  * is SSL session and false if it is local or non-ssl session.
33  */
35 Datum
37 {
39 }
40 
41 
42 /*
43  * Returns SSL version currently in use.
44  */
46 Datum
48 {
49  if (MyProcPort->ssl == NULL)
51  PG_RETURN_TEXT_P(cstring_to_text(SSL_get_version(MyProcPort->ssl)));
52 }
53 
54 
55 /*
56  * Returns SSL cipher currently in use.
57  */
59 Datum
61 {
62  if (MyProcPort->ssl == NULL)
64  PG_RETURN_TEXT_P(cstring_to_text(SSL_get_cipher(MyProcPort->ssl)));
65 }
66 
67 
68 /*
69  * Indicates whether current client provided a certificate
70  *
71  * Function has no arguments. Returns bool. True if current session
72  * is SSL session and client certificate is verified, otherwise false.
73  */
75 Datum
77 {
78  PG_RETURN_BOOL(MyProcPort->peer != NULL);
79 }
80 
81 
82 /*
83  * Returns serial number of certificate used to establish current
84  * session
85  *
86  * Function has no arguments. It returns the certificate serial
87  * number as numeric or null if current session doesn't use SSL or if
88  * SSL connection is established without sending client certificate.
89  */
91 Datum
93 {
94  Datum result;
95  Port *port = MyProcPort;
96  X509 *peer = port->peer;
97  ASN1_INTEGER *serial = NULL;
98  BIGNUM *b;
99  char *decimal;
100 
101  if (!peer)
102  PG_RETURN_NULL();
103  serial = X509_get_serialNumber(peer);
104  b = ASN1_INTEGER_to_BN(serial, NULL);
105  decimal = BN_bn2dec(b);
106 
107  BN_free(b);
109  CStringGetDatum(decimal),
110  ObjectIdGetDatum(0),
111  Int32GetDatum(-1));
112  OPENSSL_free(decimal);
113  return result;
114 }
115 
116 
117 /*
118  * Converts OpenSSL ASN1_STRING structure into text
119  *
120  * Converts ASN1_STRING into text, converting all the characters into
121  * current database encoding if possible. Any invalid characters are
122  * replaced by question marks.
123  *
124  * Parameter: str - OpenSSL ASN1_STRING structure. Memory management
125  * of this structure is responsibility of caller.
126  *
127  * Returns Datum, which can be directly returned from a C language SQL
128  * function.
129  */
130 static Datum
131 ASN1_STRING_to_text(ASN1_STRING *str)
132 {
133  BIO *membuf;
134  size_t size;
135  char nullterm;
136  char *sp;
137  char *dp;
138  text *result;
139 
140  membuf = BIO_new(BIO_s_mem());
141  (void) BIO_set_close(membuf, BIO_CLOSE);
142  ASN1_STRING_print_ex(membuf, str,
143  ((ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB)
144  | ASN1_STRFLGS_UTF8_CONVERT));
145  /* ensure null termination of the BIO's content */
146  nullterm = '\0';
147  BIO_write(membuf, &nullterm, 1);
148  size = BIO_get_mem_data(membuf, &sp);
149  dp = pg_any_to_server(sp, size - 1, PG_UTF8);
150  result = cstring_to_text(dp);
151  if (dp != sp)
152  pfree(dp);
153  BIO_free(membuf);
154 
155  PG_RETURN_TEXT_P(result);
156 }
157 
158 
159 /*
160  * Returns specified field of specified X509_NAME structure
161  *
162  * Common part of ssl_client_dn and ssl_issuer_dn functions.
163  *
164  * Parameter: X509_NAME *name - either subject or issuer of certificate
165  * Parameter: text fieldName - field name string like 'CN' or commonName
166  * to be looked up in the OpenSSL ASN1 OID database
167  *
168  * Returns result of ASN1_STRING_to_text applied to appropriate
169  * part of name
170  */
171 static Datum
172 X509_NAME_field_to_text(X509_NAME *name, text *fieldName)
173 {
174  char *string_fieldname;
175  int nid,
176  index;
177  ASN1_STRING *data;
178 
179  string_fieldname = text_to_cstring(fieldName);
180  nid = OBJ_txt2nid(string_fieldname);
181  if (nid == NID_undef)
182  ereport(ERROR,
183  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
184  errmsg("invalid X.509 field name: \"%s\"",
185  string_fieldname)));
186  pfree(string_fieldname);
187  index = X509_NAME_get_index_by_NID(name, nid, -1);
188  if (index < 0)
189  return (Datum) 0;
190  data = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, index));
191  return ASN1_STRING_to_text(data);
192 }
193 
194 
195 /*
196  * Returns specified field of client certificate distinguished name
197  *
198  * Receives field name (like 'commonName' and 'emailAddress') and
199  * returns appropriate part of certificate subject converted into
200  * database encoding.
201  *
202  * Parameter: fieldname text - will be looked up in OpenSSL object
203  * identifier database
204  *
205  * Returns text string with appropriate value.
206  *
207  * Throws an error if argument cannot be converted into ASN1 OID by
208  * OpenSSL. Returns null if no client certificate is present, or if
209  * there is no field with such name in the certificate.
210  */
212 Datum
214 {
215  text *fieldname = PG_GETARG_TEXT_P(0);
216  Datum result;
217 
218  if (!(MyProcPort->peer))
219  PG_RETURN_NULL();
220 
221  result = X509_NAME_field_to_text(X509_get_subject_name(MyProcPort->peer), fieldname);
222 
223  if (!result)
224  PG_RETURN_NULL();
225  else
226  return result;
227 }
228 
229 
230 /*
231  * Returns specified field of client certificate issuer name
232  *
233  * Receives field name (like 'commonName' and 'emailAddress') and
234  * returns appropriate part of certificate subject converted into
235  * database encoding.
236  *
237  * Parameter: fieldname text - would be looked up in OpenSSL object
238  * identifier database
239  *
240  * Returns text string with appropriate value.
241  *
242  * Throws an error if argument cannot be converted into ASN1 OID by
243  * OpenSSL. Returns null if no client certificate is present, or if
244  * there is no field with such name in the certificate.
245  */
247 Datum
249 {
250  text *fieldname = PG_GETARG_TEXT_P(0);
251  Datum result;
252 
253  if (!(MyProcPort->peer))
254  PG_RETURN_NULL();
255 
256  result = X509_NAME_field_to_text(X509_get_issuer_name(MyProcPort->peer), fieldname);
257 
258  if (!result)
259  PG_RETURN_NULL();
260  else
261  return result;
262 }
263 
264 
265 /*
266  * Equivalent of X509_NAME_oneline that respects encoding
267  *
268  * This function converts X509_NAME structure to the text variable
269  * converting all textual data into current database encoding.
270  *
271  * Parameter: X509_NAME *name X509_NAME structure to be converted
272  *
273  * Returns: text datum which contains string representation of
274  * X509_NAME
275  */
276 static Datum
278 {
279  BIO *membuf = BIO_new(BIO_s_mem());
280  int i,
281  nid,
282  count = X509_NAME_entry_count(name);
283  X509_NAME_ENTRY *e;
284  ASN1_STRING *v;
285  const char *field_name;
286  size_t size;
287  char nullterm;
288  char *sp;
289  char *dp;
290  text *result;
291 
292  (void) BIO_set_close(membuf, BIO_CLOSE);
293  for (i = 0; i < count; i++)
294  {
295  e = X509_NAME_get_entry(name, i);
296  nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(e));
297  v = X509_NAME_ENTRY_get_data(e);
298  field_name = OBJ_nid2sn(nid);
299  if (!field_name)
300  field_name = OBJ_nid2ln(nid);
301  BIO_printf(membuf, "/%s=", field_name);
302  ASN1_STRING_print_ex(membuf, v,
303  ((ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB)
304  | ASN1_STRFLGS_UTF8_CONVERT));
305  }
306 
307  /* ensure null termination of the BIO's content */
308  nullterm = '\0';
309  BIO_write(membuf, &nullterm, 1);
310  size = BIO_get_mem_data(membuf, &sp);
311  dp = pg_any_to_server(sp, size - 1, PG_UTF8);
312  result = cstring_to_text(dp);
313  if (dp != sp)
314  pfree(dp);
315  BIO_free(membuf);
316 
317  PG_RETURN_TEXT_P(result);
318 }
319 
320 
321 /*
322  * Returns current client certificate subject as one string
323  *
324  * This function returns distinguished name (subject) of the client
325  * certificate used in the current SSL connection, converting it into
326  * the current database encoding.
327  *
328  * Returns text datum.
329  */
331 Datum
333 {
334  if (!(MyProcPort->peer))
335  PG_RETURN_NULL();
336  return X509_NAME_to_text(X509_get_subject_name(MyProcPort->peer));
337 }
338 
339 
340 /*
341  * Returns current client certificate issuer as one string
342  *
343  * This function returns issuer's distinguished name of the client
344  * certificate used in the current SSL connection, converting it into
345  * the current database encoding.
346  *
347  * Returns text datum.
348  */
350 Datum
352 {
353  if (!(MyProcPort->peer))
354  PG_RETURN_NULL();
355  return X509_NAME_to_text(X509_get_issuer_name(MyProcPort->peer));
356 }