Header And Logo

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

sslinfo.c

Go to the documentation of this file.
00001 /*
00002  * module for PostgreSQL to access client SSL certificate information
00003  *
00004  * Written by Victor B. Wagner <[email protected]>, Cryptocom LTD
00005  * This file is distributed under BSD-style license.
00006  *
00007  * contrib/sslinfo/sslinfo.c
00008  */
00009 
00010 #include "postgres.h"
00011 #include "fmgr.h"
00012 #include "utils/numeric.h"
00013 #include "libpq/libpq-be.h"
00014 #include "miscadmin.h"
00015 #include "utils/builtins.h"
00016 #include "mb/pg_wchar.h"
00017 
00018 #include <openssl/x509.h>
00019 #include <openssl/asn1.h>
00020 
00021 
00022 PG_MODULE_MAGIC;
00023 
00024 
00025 Datum       ssl_is_used(PG_FUNCTION_ARGS);
00026 Datum       ssl_version(PG_FUNCTION_ARGS);
00027 Datum       ssl_cipher(PG_FUNCTION_ARGS);
00028 Datum       ssl_client_cert_present(PG_FUNCTION_ARGS);
00029 Datum       ssl_client_serial(PG_FUNCTION_ARGS);
00030 Datum       ssl_client_dn_field(PG_FUNCTION_ARGS);
00031 Datum       ssl_issuer_field(PG_FUNCTION_ARGS);
00032 Datum       ssl_client_dn(PG_FUNCTION_ARGS);
00033 Datum       ssl_issuer_dn(PG_FUNCTION_ARGS);
00034 Datum       X509_NAME_field_to_text(X509_NAME *name, text *fieldName);
00035 Datum       X509_NAME_to_text(X509_NAME *name);
00036 Datum       ASN1_STRING_to_text(ASN1_STRING *str);
00037 
00038 
00039 /*
00040  * Indicates whether current session uses SSL
00041  *
00042  * Function has no arguments.  Returns bool.  True if current session
00043  * is SSL session and false if it is local or non-ssl session.
00044  */
00045 PG_FUNCTION_INFO_V1(ssl_is_used);
00046 Datum
00047 ssl_is_used(PG_FUNCTION_ARGS)
00048 {
00049     PG_RETURN_BOOL(MyProcPort->ssl != NULL);
00050 }
00051 
00052 
00053 /*
00054  * Returns SSL cipher currently in use.
00055  */
00056 PG_FUNCTION_INFO_V1(ssl_version);
00057 Datum
00058 ssl_version(PG_FUNCTION_ARGS)
00059 {
00060     if (MyProcPort->ssl == NULL)
00061         PG_RETURN_NULL();
00062     PG_RETURN_TEXT_P(cstring_to_text(SSL_get_version(MyProcPort->ssl)));
00063 }
00064 
00065 
00066 /*
00067  * Returns SSL cipher currently in use.
00068  */
00069 PG_FUNCTION_INFO_V1(ssl_cipher);
00070 Datum
00071 ssl_cipher(PG_FUNCTION_ARGS)
00072 {
00073     if (MyProcPort->ssl == NULL)
00074         PG_RETURN_NULL();
00075     PG_RETURN_TEXT_P(cstring_to_text(SSL_get_cipher(MyProcPort->ssl)));
00076 }
00077 
00078 
00079 /*
00080  * Indicates whether current client have provided a certificate
00081  *
00082  * Function has no arguments.  Returns bool.  True if current session
00083  * is SSL session and client certificate is verified, otherwise false.
00084  */
00085 PG_FUNCTION_INFO_V1(ssl_client_cert_present);
00086 Datum
00087 ssl_client_cert_present(PG_FUNCTION_ARGS)
00088 {
00089     PG_RETURN_BOOL(MyProcPort->peer != NULL);
00090 }
00091 
00092 
00093 /*
00094  * Returns serial number of certificate used to establish current
00095  * session
00096  *
00097  * Function has no arguments.  It returns the certificate serial
00098  * number as numeric or null if current session doesn't use SSL or if
00099  * SSL connection is established without sending client certificate.
00100  */
00101 PG_FUNCTION_INFO_V1(ssl_client_serial);
00102 Datum
00103 ssl_client_serial(PG_FUNCTION_ARGS)
00104 {
00105     Datum       result;
00106     Port       *port = MyProcPort;
00107     X509       *peer = port->peer;
00108     ASN1_INTEGER *serial = NULL;
00109     BIGNUM     *b;
00110     char       *decimal;
00111 
00112     if (!peer)
00113         PG_RETURN_NULL();
00114     serial = X509_get_serialNumber(peer);
00115     b = ASN1_INTEGER_to_BN(serial, NULL);
00116     decimal = BN_bn2dec(b);
00117 
00118     BN_free(b);
00119     result = DirectFunctionCall3(numeric_in,
00120                                  CStringGetDatum(decimal),
00121                                  ObjectIdGetDatum(0),
00122                                  Int32GetDatum(-1));
00123     OPENSSL_free(decimal);
00124     return result;
00125 }
00126 
00127 
00128 /*
00129  * Converts OpenSSL ASN1_STRING structure into text
00130  *
00131  * Converts ASN1_STRING into text, converting all the characters into
00132  * current database encoding if possible.  Any invalid characters are
00133  * replaced by question marks.
00134  *
00135  * Parameter: str - OpenSSL ASN1_STRING structure.  Memory management
00136  * of this structure is responsibility of caller.
00137  *
00138  * Returns Datum, which can be directly returned from a C language SQL
00139  * function.
00140  */
00141 Datum
00142 ASN1_STRING_to_text(ASN1_STRING *str)
00143 {
00144     BIO        *membuf;
00145     size_t      size;
00146     char        nullterm;
00147     char       *sp;
00148     char       *dp;
00149     text       *result;
00150 
00151     membuf = BIO_new(BIO_s_mem());
00152     (void) BIO_set_close(membuf, BIO_CLOSE);
00153     ASN1_STRING_print_ex(membuf, str,
00154                          ((ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB)
00155                           | ASN1_STRFLGS_UTF8_CONVERT));
00156     /* ensure null termination of the BIO's content */
00157     nullterm = '\0';
00158     BIO_write(membuf, &nullterm, 1);
00159     size = BIO_get_mem_data(membuf, &sp);
00160     dp = (char *) pg_do_encoding_conversion((unsigned char *) sp,
00161                                             size - 1,
00162                                             PG_UTF8,
00163                                             GetDatabaseEncoding());
00164     result = cstring_to_text(dp);
00165     if (dp != sp)
00166         pfree(dp);
00167     BIO_free(membuf);
00168 
00169     PG_RETURN_TEXT_P(result);
00170 }
00171 
00172 
00173 /*
00174  * Returns specified field of specified X509_NAME structure
00175  *
00176  * Common part of ssl_client_dn and ssl_issuer_dn functions.
00177  *
00178  * Parameter: X509_NAME *name - either subject or issuer of certificate
00179  * Parameter: text fieldName  - field name string like 'CN' or commonName
00180  *            to be looked up in the OpenSSL ASN1 OID database
00181  *
00182  * Returns result of ASN1_STRING_to_text applied to appropriate
00183  * part of name
00184  */
00185 Datum
00186 X509_NAME_field_to_text(X509_NAME *name, text *fieldName)
00187 {
00188     char       *string_fieldname;
00189     int         nid,
00190                 index;
00191     ASN1_STRING *data;
00192 
00193     string_fieldname = text_to_cstring(fieldName);
00194     nid = OBJ_txt2nid(string_fieldname);
00195     if (nid == NID_undef)
00196         ereport(ERROR,
00197                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00198                  errmsg("invalid X.509 field name: \"%s\"",
00199                         string_fieldname)));
00200     pfree(string_fieldname);
00201     index = X509_NAME_get_index_by_NID(name, nid, -1);
00202     if (index < 0)
00203         return (Datum) 0;
00204     data = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, index));
00205     return ASN1_STRING_to_text(data);
00206 }
00207 
00208 
00209 /*
00210  * Returns specified field of client certificate distinguished name
00211  *
00212  * Receives field name (like 'commonName' and 'emailAddress') and
00213  * returns appropriate part of certificate subject converted into
00214  * database encoding.
00215  *
00216  * Parameter: fieldname text - will be looked up in OpenSSL object
00217  * identifier database
00218  *
00219  * Returns text string with appropriate value.
00220  *
00221  * Throws an error if argument cannot be converted into ASN1 OID by
00222  * OpenSSL.  Returns null if no client certificate is present, or if
00223  * there is no field with such name in the certificate.
00224  */
00225 PG_FUNCTION_INFO_V1(ssl_client_dn_field);
00226 Datum
00227 ssl_client_dn_field(PG_FUNCTION_ARGS)
00228 {
00229     text       *fieldname = PG_GETARG_TEXT_P(0);
00230     Datum       result;
00231 
00232     if (!(MyProcPort->peer))
00233         PG_RETURN_NULL();
00234 
00235     result = X509_NAME_field_to_text(X509_get_subject_name(MyProcPort->peer), fieldname);
00236 
00237     if (!result)
00238         PG_RETURN_NULL();
00239     else
00240         return result;
00241 }
00242 
00243 
00244 /*
00245  * Returns specified field of client certificate issuer name
00246  *
00247  * Receives field name (like 'commonName' and 'emailAddress') and
00248  * returns appropriate part of certificate subject converted into
00249  * database encoding.
00250  *
00251  * Parameter: fieldname text - would be looked up in OpenSSL object
00252  * identifier database
00253  *
00254  * Returns text string with appropriate value.
00255  *
00256  * Throws an error if argument cannot be converted into ASN1 OID by
00257  * OpenSSL.  Returns null if no client certificate is present, or if
00258  * there is no field with such name in the certificate.
00259  */
00260 PG_FUNCTION_INFO_V1(ssl_issuer_field);
00261 Datum
00262 ssl_issuer_field(PG_FUNCTION_ARGS)
00263 {
00264     text       *fieldname = PG_GETARG_TEXT_P(0);
00265     Datum       result;
00266 
00267     if (!(MyProcPort->peer))
00268         PG_RETURN_NULL();
00269 
00270     result = X509_NAME_field_to_text(X509_get_issuer_name(MyProcPort->peer), fieldname);
00271 
00272     if (!result)
00273         PG_RETURN_NULL();
00274     else
00275         return result;
00276 }
00277 
00278 
00279 /*
00280  * Equivalent of X509_NAME_oneline that respects encoding
00281  *
00282  * This function converts X509_NAME structure to the text variable
00283  * converting all textual data into current database encoding.
00284  *
00285  * Parameter: X509_NAME *name X509_NAME structure to be converted
00286  *
00287  * Returns: text datum which contains string representation of
00288  * X509_NAME
00289  */
00290 Datum
00291 X509_NAME_to_text(X509_NAME *name)
00292 {
00293     BIO        *membuf = BIO_new(BIO_s_mem());
00294     int         i,
00295                 nid,
00296                 count = X509_NAME_entry_count(name);
00297     X509_NAME_ENTRY *e;
00298     ASN1_STRING *v;
00299     const char *field_name;
00300     size_t      size;
00301     char        nullterm;
00302     char       *sp;
00303     char       *dp;
00304     text       *result;
00305 
00306     (void) BIO_set_close(membuf, BIO_CLOSE);
00307     for (i = 0; i < count; i++)
00308     {
00309         e = X509_NAME_get_entry(name, i);
00310         nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(e));
00311         v = X509_NAME_ENTRY_get_data(e);
00312         field_name = OBJ_nid2sn(nid);
00313         if (!field_name)
00314             field_name = OBJ_nid2ln(nid);
00315         BIO_printf(membuf, "/%s=", field_name);
00316         ASN1_STRING_print_ex(membuf, v,
00317                              ((ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB)
00318                               | ASN1_STRFLGS_UTF8_CONVERT));
00319     }
00320 
00321     /* ensure null termination of the BIO's content */
00322     nullterm = '\0';
00323     BIO_write(membuf, &nullterm, 1);
00324     size = BIO_get_mem_data(membuf, &sp);
00325     dp = (char *) pg_do_encoding_conversion((unsigned char *) sp,
00326                                             size - 1,
00327                                             PG_UTF8,
00328                                             GetDatabaseEncoding());
00329     result = cstring_to_text(dp);
00330     if (dp != sp)
00331         pfree(dp);
00332     BIO_free(membuf);
00333 
00334     PG_RETURN_TEXT_P(result);
00335 }
00336 
00337 
00338 /*
00339  * Returns current client certificate subject as one string
00340  *
00341  * This function returns distinguished name (subject) of the client
00342  * certificate used in the current SSL connection, converting it into
00343  * the current database encoding.
00344  *
00345  * Returns text datum.
00346  */
00347 PG_FUNCTION_INFO_V1(ssl_client_dn);
00348 Datum
00349 ssl_client_dn(PG_FUNCTION_ARGS)
00350 {
00351     if (!(MyProcPort->peer))
00352         PG_RETURN_NULL();
00353     return X509_NAME_to_text(X509_get_subject_name(MyProcPort->peer));
00354 }
00355 
00356 
00357 /*
00358  * Returns current client certificate issuer as one string
00359  *
00360  * This function returns issuer's distinguished name of the client
00361  * certificate used in the current SSL connection, converting it into
00362  * the current database encoding.
00363  *
00364  * Returns text datum.
00365  */
00366 PG_FUNCTION_INFO_V1(ssl_issuer_dn);
00367 Datum
00368 ssl_issuer_dn(PG_FUNCTION_ARGS)
00369 {
00370     if (!(MyProcPort->peer))
00371         PG_RETURN_NULL();
00372     return X509_NAME_to_text(X509_get_issuer_name(MyProcPort->peer));
00373 }