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