Add support for using SSL client certificates to authenticate to the

database (only for SSL connections, obviously).
This commit is contained in:
Magnus Hagander 2008-11-20 11:48:26 +00:00
parent 3c486fbd1c
commit f179d5ea99
6 changed files with 120 additions and 14 deletions

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/client-auth.sgml,v 1.111 2008/11/18 13:10:20 petere Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/client-auth.sgml,v 1.112 2008/11/20 11:48:26 mha Exp $ -->
<chapter id="client-authentication"> <chapter id="client-authentication">
<title>Client Authentication</title> <title>Client Authentication</title>
@ -387,6 +387,16 @@ hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><literal>cert</></term>
<listitem>
<para>
Authenticate using SSL client certificates. See
<xref linkend="auth-cert"> for details.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><literal>pam</></term> <term><literal>pam</></term>
<listitem> <listitem>
@ -1114,6 +1124,25 @@ ldapserver=ldap.example.net prefix="cn=" suffix="dc=example, dc=net"
</sect2> </sect2>
<sect2 id="auth-cert">
<title>Certificate authentication</title>
<indexterm zone="auth-cert">
<primary>Certificate</primary>
</indexterm>
<para>
This authentication method uses SSL client certificates to perform
authentication. It is therefore only available for SSL connections.
When using this authentication method, the server will require that
the client provide a certificate. No password prompt will be sent
to the client. The <literal>cn</literal> attribute of the certificate
will be matched with the username the user is trying to log in as,
and if they match the login will be allowed. Username mapping can be
used if the usernames don't match.
</para>
</sect2>
<sect2 id="auth-pam"> <sect2 id="auth-pam">
<title>PAM authentication</title> <title>PAM authentication</title>

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.421 2008/11/20 09:29:35 mha Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.422 2008/11/20 11:48:26 mha Exp $ -->
<chapter Id="runtime"> <chapter Id="runtime">
<title>Operating System Environment</title> <title>Operating System Environment</title>
@ -1674,11 +1674,9 @@ $ <userinput>kill -INT `head -1 /usr/local/pgsql/data/postmaster.pid`</userinput
</para> </para>
<para> <para>
<productname>PostgreSQL</> currently does not support authentication You can use the authentication method <literal>cert</> to use the
using client certificates, since it cannot differentiate between client certificate for authenticating users. See
different users. As long as the user holds any certificate issued <xref linkend="auth-cert"> for details.
by a trusted CA it will be accepted, regardless of what account the
user is trying to connect with.
</para> </para>
</sect2> </sect2>

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.172 2008/11/20 09:29:36 mha Exp $ * $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.173 2008/11/20 11:48:26 mha Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -113,6 +113,14 @@ ULONG(*__ldap_start_tls_sA) (
static int CheckLDAPAuth(Port *port); static int CheckLDAPAuth(Port *port);
#endif /* USE_LDAP */ #endif /* USE_LDAP */
/*----------------------------------------------------------------
* Cert authentication
*----------------------------------------------------------------
*/
#ifdef USE_SSL
static int CheckCertAuth(Port *port);
#endif
/*---------------------------------------------------------------- /*----------------------------------------------------------------
* Kerberos and GSSAPI GUCs * Kerberos and GSSAPI GUCs
@ -431,6 +439,14 @@ ClientAuthentication(Port *port)
#endif #endif
break; break;
case uaCert:
#ifdef USE_SSL
status = CheckCertAuth(port);
#else
Assert(false);
#endif
break;
case uaTrust: case uaTrust:
status = STATUS_OK; status = STATUS_OK;
break; break;
@ -2120,3 +2136,28 @@ CheckLDAPAuth(Port *port)
} }
#endif /* USE_LDAP */ #endif /* USE_LDAP */
/*----------------------------------------------------------------
* SSL client certificate authentication
*----------------------------------------------------------------
*/
#ifdef USE_SSL
static int
CheckCertAuth(Port *port)
{
Assert(port->ssl);
/* Make sure we have received a username in the certificate */
if (port->peer_cn == NULL ||
strlen(port->peer_cn) <= 0)
{
ereport(LOG,
(errmsg("Certificate login failed for user \"%s\": client certificate contains no username",
port->user_name)));
return STATUS_ERROR;
}
/* Just pass the certificate CN to the usermap check */
return check_usermap(port->hba->usermap, port->user_name, port->peer_cn, false);
}
#endif

View File

@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.173 2008/11/20 09:29:36 mha Exp $ * $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.174 2008/11/20 11:48:26 mha Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -858,6 +858,12 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
parsedline->auth_method = uaLDAP; parsedline->auth_method = uaLDAP;
#else #else
unsupauth = "ldap"; unsupauth = "ldap";
#endif
else if (strcmp(token, "cert") == 0)
#ifdef USE_SSL
parsedline->auth_method = uaCert;
#else
unsupauth = "cert";
#endif #endif
else else
{ {
@ -893,6 +899,17 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
return false; return false;
} }
if (parsedline->conntype != ctHostSSL &&
parsedline->auth_method == uaCert)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("cert authentication is only supported on hostssl connections"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
return false;
}
/* Parse remaining arguments */ /* Parse remaining arguments */
while ((line_item = lnext(line_item)) != NULL) while ((line_item = lnext(line_item)) != NULL)
{ {
@ -923,8 +940,9 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
if (parsedline->auth_method != uaIdent && if (parsedline->auth_method != uaIdent &&
parsedline->auth_method != uaKrb5 && parsedline->auth_method != uaKrb5 &&
parsedline->auth_method != uaGSS && parsedline->auth_method != uaGSS &&
parsedline->auth_method != uaSSPI) parsedline->auth_method != uaSSPI &&
INVALID_AUTH_OPTION("map", "ident, krb5, gssapi and sspi"); parsedline->auth_method != uaCert)
INVALID_AUTH_OPTION("map", "ident, krb5, gssapi, sspi and cert");
parsedline->usermap = pstrdup(c); parsedline->usermap = pstrdup(c);
} }
else if (strcmp(token, "clientcert") == 0) else if (strcmp(token, "clientcert") == 0)
@ -957,7 +975,18 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
parsedline->clientcert = true; parsedline->clientcert = true;
} }
else else
{
if (parsedline->auth_method == uaCert)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("clientcert can not be set to 0 when using \"cert\" authentication"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
return false;
}
parsedline->clientcert = false; parsedline->clientcert = false;
}
} }
else if (strcmp(token, "pamservice") == 0) else if (strcmp(token, "pamservice") == 0)
{ {
@ -1021,6 +1050,14 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
{ {
MANDATORY_AUTH_ARG(parsedline->ldapserver, "ldapserver", "ldap"); MANDATORY_AUTH_ARG(parsedline->ldapserver, "ldapserver", "ldap");
} }
/*
* Enforce any parameters implied by other settings.
*/
if (parsedline->auth_method == uaCert)
{
parsedline->clientcert = true;
}
return true; return true;
} }

View File

@ -35,7 +35,7 @@
# an IP address and netmask in separate columns to specify the set of hosts. # an IP address and netmask in separate columns to specify the set of hosts.
# #
# METHOD can be "trust", "reject", "md5", "crypt", "password", "gss", "sspi", # METHOD can be "trust", "reject", "md5", "crypt", "password", "gss", "sspi",
# "krb5", "ident", "pam" or "ldap". Note that "password" sends passwords # "krb5", "ident", "pam", "ldap" or "cert". Note that "password" sends passwords
# in clear text; "md5" is preferred since it sends encrypted passwords. # in clear text; "md5" is preferred since it sends encrypted passwords.
# #
# OPTIONS are a set of options for the authentication in the format # OPTIONS are a set of options for the authentication in the format

View File

@ -4,7 +4,7 @@
* Interface to hba.c * Interface to hba.c
* *
* *
* $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.52 2008/11/20 09:29:36 mha Exp $ * $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.53 2008/11/20 11:48:26 mha Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -26,7 +26,8 @@ typedef enum UserAuth
uaGSS, uaGSS,
uaSSPI, uaSSPI,
uaPAM, uaPAM,
uaLDAP uaLDAP,
uaCert
} UserAuth; } UserAuth;
typedef enum ConnType typedef enum ConnType