mirror of
https://github.com/postgres/postgres.git
synced 2025-11-21 00:42:43 +03:00
Allow matching the DN of a client certificate for authentication
Currently we only recognize the Common Name (CN) of a certificate's subject to be matched against the user name. Thus certificates with subjects '/OU=eng/CN=fred' and '/OU=sales/CN=fred' will have the same connection rights. This patch provides an option to match the whole Distinguished Name (DN) instead of just the CN. On any hba line using client certificate identity, there is an option 'clientname' which can have values of 'DN' or 'CN'. The default is 'CN', the current procedure. The DN is matched against the RFC2253 formatted DN, which looks like 'CN=fred,OU=eng'. This facility of probably best used in conjunction with an ident map. Discussion: https://postgr.es/m/92e70110-9273-d93c-5913-0bccb6562740@dunslane.net Reviewed-By: Michael Paquier, Daniel Gustafsson, Jacob Champion
This commit is contained in:
@@ -551,22 +551,26 @@ aloop:
|
||||
/* Get client certificate, if available. */
|
||||
port->peer = SSL_get_peer_certificate(port->ssl);
|
||||
|
||||
/* and extract the Common Name from it. */
|
||||
/* and extract the Common Name and Distinguished Name from it. */
|
||||
port->peer_cn = NULL;
|
||||
port->peer_dn = NULL;
|
||||
port->peer_cert_valid = false;
|
||||
if (port->peer != NULL)
|
||||
{
|
||||
int len;
|
||||
X509_NAME *x509name = X509_get_subject_name(port->peer);
|
||||
char *peer_dn;
|
||||
BIO *bio = NULL;
|
||||
BUF_MEM *bio_buf = NULL;
|
||||
|
||||
len = X509_NAME_get_text_by_NID(X509_get_subject_name(port->peer),
|
||||
NID_commonName, NULL, 0);
|
||||
len = X509_NAME_get_text_by_NID(x509name, NID_commonName, NULL, 0);
|
||||
if (len != -1)
|
||||
{
|
||||
char *peer_cn;
|
||||
|
||||
peer_cn = MemoryContextAlloc(TopMemoryContext, len + 1);
|
||||
r = X509_NAME_get_text_by_NID(X509_get_subject_name(port->peer),
|
||||
NID_commonName, peer_cn, len + 1);
|
||||
r = X509_NAME_get_text_by_NID(x509name, NID_commonName, peer_cn,
|
||||
len + 1);
|
||||
peer_cn[len] = '\0';
|
||||
if (r != len)
|
||||
{
|
||||
@@ -590,6 +594,47 @@ aloop:
|
||||
|
||||
port->peer_cn = peer_cn;
|
||||
}
|
||||
|
||||
bio = BIO_new(BIO_s_mem());
|
||||
if (!bio)
|
||||
{
|
||||
pfree(port->peer_cn);
|
||||
port->peer_cn = NULL;
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
* RFC2253 is the closest thing to an accepted standard format for
|
||||
* DNs. We have documented how to produce this format from a
|
||||
* certificate. It uses commas instead of slashes for delimiters,
|
||||
* which make regular expression matching a bit easier. Also note that
|
||||
* it prints the Subject fields in reverse order.
|
||||
*/
|
||||
X509_NAME_print_ex(bio, x509name, 0, XN_FLAG_RFC2253);
|
||||
if (BIO_get_mem_ptr(bio, &bio_buf) <= 0)
|
||||
{
|
||||
BIO_free(bio);
|
||||
pfree(port->peer_cn);
|
||||
port->peer_cn = NULL;
|
||||
return -1;
|
||||
}
|
||||
peer_dn = MemoryContextAlloc(TopMemoryContext, bio_buf->length + 1);
|
||||
memcpy(peer_dn, bio_buf->data, bio_buf->length);
|
||||
len = bio_buf->length;
|
||||
BIO_free(bio);
|
||||
peer_dn[len] = '\0';
|
||||
if (len != strlen(peer_dn))
|
||||
{
|
||||
ereport(COMMERROR,
|
||||
(errcode(ERRCODE_PROTOCOL_VIOLATION),
|
||||
errmsg("SSL certificate's distinguished name contains embedded null")));
|
||||
pfree(peer_dn);
|
||||
pfree(port->peer_cn);
|
||||
port->peer_cn = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
port->peer_dn = peer_dn;
|
||||
|
||||
port->peer_cert_valid = true;
|
||||
}
|
||||
|
||||
@@ -618,6 +663,12 @@ be_tls_close(Port *port)
|
||||
pfree(port->peer_cn);
|
||||
port->peer_cn = NULL;
|
||||
}
|
||||
|
||||
if (port->peer_dn)
|
||||
{
|
||||
pfree(port->peer_dn);
|
||||
port->peer_dn = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t
|
||||
|
||||
Reference in New Issue
Block a user