mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-02-17 19:30:00 +08:00
sslinfo contrib module - information about current SSL certificate
Author: Victor Wagner <vitus@cryptocom.ru>
This commit is contained in:
parent
c8041474ee
commit
6dc920de59
@ -1,4 +1,4 @@
|
||||
# $PostgreSQL: pgsql/contrib/Makefile,v 1.66 2006/05/30 13:25:57 momjian Exp $
|
||||
# $PostgreSQL: pgsql/contrib/Makefile,v 1.67 2006/09/04 15:07:46 petere Exp $
|
||||
|
||||
subdir = contrib
|
||||
top_builddir = ..
|
||||
@ -36,6 +36,10 @@ WANTED_DIRS = \
|
||||
userlock \
|
||||
vacuumlo
|
||||
|
||||
ifeq ($(with_openssl),yes)
|
||||
WANTED_DIRS += sslinfo
|
||||
endif
|
||||
|
||||
# Missing:
|
||||
# adddepend \ (does not have a makefile)
|
||||
# mSQL-interface \ (requires msql installed)
|
||||
|
9
contrib/sslinfo/Makefile
Normal file
9
contrib/sslinfo/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
subdir = contrib/sslinfo
|
||||
top_builddir = ../..
|
||||
include $(top_builddir)/src/Makefile.global
|
||||
|
||||
MODULES = sslinfo
|
||||
DATA_built = sslinfo.sql
|
||||
DOC = README.pgsslinfo
|
||||
|
||||
include ../contrib-global.mk
|
121
contrib/sslinfo/README.sslinfo
Normal file
121
contrib/sslinfo/README.sslinfo
Normal file
@ -0,0 +1,121 @@
|
||||
sslinfo - information about current SSL certificate for PostgreSQL
|
||||
==================================================================
|
||||
Copyright (c) 2006 Cryptocom LTD
|
||||
Author: Victor Wagner <vitus@cryptocom.ru>
|
||||
E-Mail of Cryptocom OpenSSL development group: <openssl@cryptocom.ru>
|
||||
|
||||
|
||||
1. Notes
|
||||
--------
|
||||
This extension won't build unless your PostgreSQL server is configured
|
||||
with --with-openssl. Information provided with these functions would
|
||||
be completely useless if you don't use SSL to connect to database.
|
||||
|
||||
|
||||
2. Functions Description
|
||||
------------------------
|
||||
|
||||
2.1. ssl_is_used()
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
ssl_is_used() RETURNS boolean;
|
||||
|
||||
Returns TRUE, if current connection to server uses SSL and FALSE
|
||||
otherwise.
|
||||
|
||||
2.2. ssl_client_cert_present()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
ssl_client_cert_present() RETURNS boolean
|
||||
|
||||
Returns TRUE if current client have presented valid SSL client
|
||||
certificate to the server and FALSE otherwise (e.g., no SSL,
|
||||
certificate hadn't be requested by server).
|
||||
|
||||
2.3. ssl_client_serial()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
ssl_client_serial() RETURNS numeric
|
||||
|
||||
Returns serial number of current client certificate. The combination
|
||||
of certificate serial number and certificate issuer is guaranteed to
|
||||
uniquely identify certificate (but not its owner -- the owner ought to
|
||||
regularily change his keys, and get new certificates from the issuer).
|
||||
|
||||
So, if you run you own CA and allow only certificates from this CA to
|
||||
be accepted by server, the serial number is the most reliable (albeit
|
||||
not very mnemonic) means to indentify user.
|
||||
|
||||
2.4. ssl_client_dn()
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
ssl_client_dn() RETURNS text
|
||||
|
||||
Returns the full subject of current client certificate, converting
|
||||
character data into the current database encoding. It is assumed that
|
||||
if you use non-Latin characters in the certificate names, your
|
||||
database is able to represent these characters, too. If your database
|
||||
uses the SQL_ASCII encoding, non-Latin characters in the name will be
|
||||
represented as UTF-8 sequences.
|
||||
|
||||
The result looks like '/CN=Somebody /C=Some country/O=Some organization'.
|
||||
|
||||
2.5. ssl_issuer_dn()
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Returns the full issuer name of the client certificate, converting
|
||||
character data into current database encoding.
|
||||
|
||||
The combination of the return value of this function with the
|
||||
certificate serial number uniquely identifies the certificate.
|
||||
|
||||
The result of this function is really useful only if you have more
|
||||
than one trusted CA certificate in your server's root.crt file, or if
|
||||
this CA has issued some intermediate certificate authority
|
||||
certificates.
|
||||
|
||||
2.6. ssl_client_dn_field()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
ssl_client_dn_field(fieldName text) RETURNS text
|
||||
|
||||
This function returns the value of the specified field in the
|
||||
certificate subject. Field names are string constants that are
|
||||
converted into ASN1 object identificators using the OpenSSL object
|
||||
database. The following values are acceptable:
|
||||
|
||||
commonName (alias CN)
|
||||
surname (alias SN)
|
||||
name
|
||||
givenName (alias GN)
|
||||
countryName (alias C)
|
||||
localityName (alias L)
|
||||
stateOrProvinceName (alias ST)
|
||||
organizationName (alias O)
|
||||
organizationUnitName (alias OU)
|
||||
title
|
||||
description
|
||||
initials
|
||||
postalCode
|
||||
streetAddress
|
||||
generationQualifier
|
||||
description
|
||||
dnQualifier
|
||||
x500UniqueIdentifier
|
||||
pseudonim
|
||||
role
|
||||
emailAddress
|
||||
|
||||
All of these fields are optional, except commonName. It depends
|
||||
entirely on your CA policy which of them would be included and which
|
||||
wouldn't. The meaning of these fields, howeer, is strictly defined by
|
||||
the X.500 and X.509 standards, so you cannot just assign arbitrary
|
||||
meaning to them.
|
||||
|
||||
2.7 ssl_issuer_field()
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
ssl_issuer_field(fieldName text) RETURNS text;
|
||||
|
||||
Does same as ssl_client_dn_field, but for the certificate issuer
|
||||
rather than the certificate subject.
|
339
contrib/sslinfo/sslinfo.c
Normal file
339
contrib/sslinfo/sslinfo.c
Normal file
@ -0,0 +1,339 @@
|
||||
/*
|
||||
* module for PostgreSQL to access client SSL certificate information
|
||||
*
|
||||
* Copyright (c) Cryptocom LTD, 2006
|
||||
* Written by Victor B. Wagner <vitus@cryptocom.ru>
|
||||
* This file is distributed under BSD-style license.
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
#include "fmgr.h"
|
||||
#include "utils/numeric.h"
|
||||
#include "libpq/libpq-be.h"
|
||||
#include "miscadmin.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "mb/pg_wchar.h"
|
||||
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/asn1.h>
|
||||
|
||||
|
||||
PG_MODULE_MAGIC;
|
||||
|
||||
|
||||
Datum ssl_is_used(PG_FUNCTION_ARGS);
|
||||
Datum ssl_client_cert_present(PG_FUNCTION_ARGS);
|
||||
Datum ssl_client_serial(PG_FUNCTION_ARGS);
|
||||
Datum ssl_client_dn_field(PG_FUNCTION_ARGS);
|
||||
Datum ssl_issuer_field(PG_FUNCTION_ARGS);
|
||||
Datum ssl_client_dn(PG_FUNCTION_ARGS);
|
||||
Datum ssl_issuer_dn(PG_FUNCTION_ARGS);
|
||||
Datum X509_NAME_field_to_text(X509_NAME *name, text *fieldName);
|
||||
Datum X509_NAME_to_text(X509_NAME *name);
|
||||
Datum ASN1_STRING_to_text(ASN1_STRING *str);
|
||||
|
||||
|
||||
/*
|
||||
* Indicates whether current session uses SSL
|
||||
*
|
||||
* Function has no arguments. Returns bool. True if current session
|
||||
* is SSL session and false if it is local or non-ssl session.
|
||||
*/
|
||||
PG_FUNCTION_INFO_V1(ssl_is_used);
|
||||
Datum ssl_is_used(PG_FUNCTION_ARGS)
|
||||
{
|
||||
PG_RETURN_BOOL(MyProcPort->ssl !=NULL);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Indicates whether current client have provided a certificate
|
||||
*
|
||||
* Function has no arguments. Returns bool. True if current session
|
||||
* is SSL session and client certificate is verified, otherwise false.
|
||||
*/
|
||||
PG_FUNCTION_INFO_V1(ssl_client_cert_present);
|
||||
Datum ssl_client_cert_present(PG_FUNCTION_ARGS)
|
||||
{
|
||||
PG_RETURN_BOOL(MyProcPort->peer != NULL);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns serial number of certificate used to establish current
|
||||
* session
|
||||
*
|
||||
* Function has no arguments. It returns the certificate serial
|
||||
* number as numeric or null if current session doesn't use SSL or if
|
||||
* SSL connection is established without sending client certificate.
|
||||
*/
|
||||
PG_FUNCTION_INFO_V1(ssl_client_serial);
|
||||
Datum ssl_client_serial(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Datum result;
|
||||
Port *port = MyProcPort;
|
||||
X509 *peer = port->peer;
|
||||
ASN1_INTEGER *serial = NULL;
|
||||
BIGNUM *b;
|
||||
char *decimal;
|
||||
|
||||
if (!peer)
|
||||
PG_RETURN_NULL();
|
||||
serial = X509_get_serialNumber(peer);
|
||||
b = ASN1_INTEGER_to_BN(serial, NULL);
|
||||
decimal = BN_bn2dec(b);
|
||||
BN_free(b);
|
||||
result = DirectFunctionCall3(numeric_in,
|
||||
CStringGetDatum(decimal),
|
||||
ObjectIdGetDatum(0),
|
||||
Int32GetDatum(-1));
|
||||
OPENSSL_free(decimal);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Converts OpenSSL ASN1_STRING structure into text
|
||||
*
|
||||
* Converts ASN1_STRING into text, converting all the characters into
|
||||
* current database encoding if possible. Any invalid characters are
|
||||
* replaced by question marks.
|
||||
*
|
||||
* Parameter: str - OpenSSL ASN1_STRING structure. Memory managment
|
||||
* of this structure is responsibility of caller.
|
||||
*
|
||||
* Returns Datum, which can be directly returned from a C language SQL
|
||||
* function.
|
||||
*/
|
||||
Datum ASN1_STRING_to_text(ASN1_STRING *str)
|
||||
{
|
||||
BIO *membuf = NULL;
|
||||
size_t size, outlen;
|
||||
char *sp;
|
||||
char *dp;
|
||||
text *result;
|
||||
|
||||
membuf = BIO_new(BIO_s_mem());
|
||||
BIO_set_close(membuf, BIO_CLOSE);
|
||||
ASN1_STRING_print_ex(membuf,str,
|
||||
((ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB)
|
||||
| ASN1_STRFLGS_UTF8_CONVERT));
|
||||
|
||||
outlen = 0;
|
||||
BIO_write(membuf, &outlen, 1);
|
||||
size = BIO_get_mem_data(membuf, &sp);
|
||||
dp = pg_do_encoding_conversion(sp, size-1, PG_UTF8, GetDatabaseEncoding());
|
||||
outlen = strlen(dp);
|
||||
result = palloc(VARHDRSZ + outlen);
|
||||
memcpy(VARDATA(result), dp, outlen);
|
||||
if (dp != sp)
|
||||
pfree(dp);
|
||||
|
||||
BIO_free(membuf);
|
||||
VARATT_SIZEP(result) = outlen + VARHDRSZ;
|
||||
PG_RETURN_TEXT_P(result);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns specified field of specified X509_NAME structure
|
||||
*
|
||||
* Common part of ssl_client_dn and ssl_issuer_dn functions.
|
||||
*
|
||||
* Parameter: X509_NAME *name - either subject or issuer of certificate
|
||||
* Parameter: text fieldName - field name string like 'CN' or commonName
|
||||
* to be looked up in the OpenSSL ASN1 OID database
|
||||
*
|
||||
* Returns result of ASN1_STRING_to_text applied to appropriate
|
||||
* part of name
|
||||
*/
|
||||
Datum X509_NAME_field_to_text(X509_NAME *name, text *fieldName)
|
||||
{
|
||||
char *sp;
|
||||
char *string_fieldname;
|
||||
char *dp;
|
||||
size_t name_len = VARSIZE(fieldName) - VARHDRSZ;
|
||||
int nid, index, i;
|
||||
ASN1_STRING *data;
|
||||
|
||||
string_fieldname = palloc(name_len + 1);
|
||||
sp = VARDATA(fieldName);
|
||||
dp = string_fieldname;
|
||||
for (i = 0; i < name_len; i++)
|
||||
*dp++ = *sp++;
|
||||
*dp = '\0';
|
||||
nid = OBJ_txt2nid(string_fieldname);
|
||||
if (nid == NID_undef)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("invalid X.509 field name: \"%s\"",
|
||||
string_fieldname)));
|
||||
pfree(string_fieldname);
|
||||
index = X509_NAME_get_index_by_NID(name, nid, -1);
|
||||
if (index < 0)
|
||||
return (Datum)0;
|
||||
data = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, index));
|
||||
return ASN1_STRING_to_text(data);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns specified field of client certificate distinguished name
|
||||
*
|
||||
* Receives field name (like 'commonName' and 'emailAddress') and
|
||||
* returns appropriate part of certificate subject converted into
|
||||
* database encoding.
|
||||
*
|
||||
* Parameter: fieldname text - will be looked up in OpenSSL object
|
||||
* identifier database
|
||||
*
|
||||
* Returns text string with appropriate value.
|
||||
*
|
||||
* Throws an error if argument cannot be converted into ASN1 OID by
|
||||
* OpenSSL. Returns null if no client certificate is present, or if
|
||||
* there is no field with such name in the certificate.
|
||||
*/
|
||||
PG_FUNCTION_INFO_V1(ssl_client_dn_field);
|
||||
Datum ssl_client_dn_field(PG_FUNCTION_ARGS)
|
||||
{
|
||||
text *fieldname = PG_GETARG_TEXT_P(0);
|
||||
Datum result;
|
||||
|
||||
if (!(MyProcPort->peer))
|
||||
PG_RETURN_NULL();
|
||||
|
||||
result = X509_NAME_field_to_text(X509_get_subject_name(MyProcPort->peer), fieldname);
|
||||
|
||||
if (!result)
|
||||
PG_RETURN_NULL();
|
||||
else
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns specified field of client certificate issuer name
|
||||
*
|
||||
* Receives field name (like 'commonName' and 'emailAddress') and
|
||||
* returns appropriate part of certificate subject converted into
|
||||
* database encoding.
|
||||
*
|
||||
* Parameter: fieldname text - would be looked up in OpenSSL object
|
||||
* identifier database
|
||||
*
|
||||
* Returns text string with appropriate value.
|
||||
*
|
||||
* Throws an error if argument cannot be converted into ASN1 OID by
|
||||
* OpenSSL. Returns null if no client certificate is present, or if
|
||||
* there is no field with such name in the certificate.
|
||||
*/
|
||||
PG_FUNCTION_INFO_V1(ssl_issuer_field);
|
||||
Datum ssl_issuer_field(PG_FUNCTION_ARGS)
|
||||
{
|
||||
text *fieldname = PG_GETARG_TEXT_P(0);
|
||||
Datum result;
|
||||
|
||||
if (!(MyProcPort->peer))
|
||||
PG_RETURN_NULL();
|
||||
|
||||
result = X509_NAME_field_to_text(X509_get_issuer_name(MyProcPort->peer), fieldname);
|
||||
|
||||
if (!result)
|
||||
PG_RETURN_NULL();
|
||||
else
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Equivalent of X509_NAME_oneline that respects encoding
|
||||
*
|
||||
* This function converts X509_NAME structure to the text variable
|
||||
* converting all textual data into current database encoding.
|
||||
*
|
||||
* Parameter: X509_NAME *name X509_NAME structure to be converted
|
||||
*
|
||||
* Returns: text datum which contains string representation of
|
||||
* X509_NAME
|
||||
*/
|
||||
Datum X509_NAME_to_text(X509_NAME *name)
|
||||
{
|
||||
BIO *membuf = BIO_new(BIO_s_mem());
|
||||
int i,nid,count = X509_NAME_entry_count(name);
|
||||
X509_NAME_ENTRY *e;
|
||||
ASN1_STRING *v;
|
||||
|
||||
const char *field_name;
|
||||
size_t size,outlen;
|
||||
char *sp;
|
||||
char *dp;
|
||||
text *result;
|
||||
|
||||
BIO_set_close(membuf, BIO_CLOSE);
|
||||
for (i=0; i<count; i++)
|
||||
{
|
||||
e = X509_NAME_get_entry(name, i);
|
||||
nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(e));
|
||||
v = X509_NAME_ENTRY_get_data(e);
|
||||
field_name = OBJ_nid2sn(nid);
|
||||
if (!field_name)
|
||||
field_name = OBJ_nid2ln(nid);
|
||||
BIO_printf(membuf, "/%s=", field_name);
|
||||
ASN1_STRING_print_ex(membuf,v,
|
||||
((ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB)
|
||||
| ASN1_STRFLGS_UTF8_CONVERT));
|
||||
}
|
||||
|
||||
i=0;
|
||||
BIO_write(membuf, &i, 1);
|
||||
size = BIO_get_mem_data(membuf, &sp);
|
||||
|
||||
dp = pg_do_encoding_conversion(sp, size-1, PG_UTF8, GetDatabaseEncoding());
|
||||
BIO_free(membuf);
|
||||
outlen = strlen(dp);
|
||||
result = palloc(VARHDRSZ + outlen);
|
||||
memcpy(VARDATA(result), dp, outlen);
|
||||
|
||||
/* pg_do_encoding_conversion has annoying habit of returning
|
||||
* source pointer */
|
||||
if (dp != sp)
|
||||
pfree(dp);
|
||||
VARATT_SIZEP(result) = outlen + VARHDRSZ;
|
||||
PG_RETURN_TEXT_P(result);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns current client certificate subject as one string
|
||||
*
|
||||
* This function returns distinguished name (subject) of the client
|
||||
* certificate used in the current SSL connection, converting it into
|
||||
* the current database encoding.
|
||||
*
|
||||
* Returns text datum.
|
||||
*/
|
||||
PG_FUNCTION_INFO_V1(ssl_client_dn);
|
||||
Datum ssl_client_dn(PG_FUNCTION_ARGS)
|
||||
{
|
||||
if (!(MyProcPort->peer))
|
||||
PG_RETURN_NULL();
|
||||
return X509_NAME_to_text(X509_get_subject_name(MyProcPort->peer));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns current client certificate issuer as one string
|
||||
*
|
||||
* This function returns issuer's distinguished name of the client
|
||||
* certificate used in the current SSL connection, converting it into
|
||||
* the current database encoding.
|
||||
*
|
||||
* Returns text datum.
|
||||
*/
|
||||
PG_FUNCTION_INFO_V1(ssl_issuer_dn);
|
||||
Datum ssl_issuer_dn(PG_FUNCTION_ARGS)
|
||||
{
|
||||
if (!(MyProcPort->peer))
|
||||
PG_RETURN_NULL();
|
||||
return X509_NAME_to_text(X509_get_issuer_name(MyProcPort->peer));
|
||||
}
|
29
contrib/sslinfo/sslinfo.sql.in
Normal file
29
contrib/sslinfo/sslinfo.sql.in
Normal file
@ -0,0 +1,29 @@
|
||||
SET search_path = public;
|
||||
|
||||
CREATE OR REPLACE FUNCTION ssl_client_serial() RETURNS numeric
|
||||
LANGUAGE C STRICT
|
||||
AS 'MODULE_PATHNAME', 'ssl_client_serial';
|
||||
|
||||
CREATE OR REPLACE FUNCTION ssl_is_used() RETURNS boolean
|
||||
LANGUAGE C STRICT
|
||||
AS 'MODULE_PATHNAME', 'ssl_is_used';
|
||||
|
||||
CREATE OR REPLACE FUNCTION ssl_client_cert_present() RETURNS boolean
|
||||
LANGUAGE C STRICT
|
||||
AS 'MODULE_PATHNAME', 'ssl_client_cert_present';
|
||||
|
||||
CREATE OR REPLACE FUNCTION ssl_client_dn_field(text) RETURNS text
|
||||
LANGUAGE C STRICT
|
||||
AS 'MODULE_PATHNAME', 'ssl_client_dn_field';
|
||||
|
||||
CREATE OR REPLACE FUNCTION ssl_issuer_field(text) RETURNS text
|
||||
LANGUAGE C STRICT
|
||||
AS 'MODULE_PATHNAME', 'ssl_issuer_field';
|
||||
|
||||
CREATE OR REPLACE FUNCTION ssl_client_dn() RETURNS text
|
||||
LANGUAGE C STRICT
|
||||
AS 'MODULE_PATHNAME', 'ssl_client_dn';
|
||||
|
||||
CREATE OR REPLACE FUNCTION ssl_issuer_dn() RETURNS text
|
||||
LANGUAGE C STRICT
|
||||
AS 'MODULE_PATHNAME', 'ssl_issuer_dn';
|
@ -13,7 +13,7 @@
|
||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.188 2006/08/15 18:26:59 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.189 2006/09/04 15:07:46 petere Exp $
|
||||
*
|
||||
* NOTES
|
||||
* some of the information in this file should be moved to other files.
|
||||
@ -131,7 +131,7 @@ extern DLLIMPORT int NBuffers;
|
||||
extern int MaxBackends;
|
||||
|
||||
extern DLLIMPORT int MyProcPid;
|
||||
extern struct Port *MyProcPort;
|
||||
extern DLLIMPORT struct Port *MyProcPort;
|
||||
extern long MyCancelKey;
|
||||
|
||||
extern char OutputFileName[];
|
||||
|
Loading…
Reference in New Issue
Block a user