diff options
author | Magnus Hagander | 2008-11-20 11:48:26 +0000 |
---|---|---|
committer | Magnus Hagander | 2008-11-20 11:48:26 +0000 |
commit | f86d99a20e12e3da546c3967f7971c9b317539b4 (patch) | |
tree | 594ec4e8b66bfe8e7d0cb7a6bd6edc4e29208df0 | |
parent | be3176d67c9af218158a98745974dc7ea647894f (diff) |
Add support for using SSL client certificates to authenticate to the
database (only for SSL connections, obviously).
-rw-r--r-- | doc/src/sgml/client-auth.sgml | 29 | ||||
-rw-r--r-- | doc/src/sgml/runtime.sgml | 8 | ||||
-rw-r--r-- | src/backend/libpq/auth.c | 41 | ||||
-rw-r--r-- | src/backend/libpq/hba.c | 41 | ||||
-rw-r--r-- | src/backend/libpq/pg_hba.conf.sample | 2 | ||||
-rw-r--r-- | src/include/libpq/hba.h | 3 |
6 files changed, 115 insertions, 9 deletions
diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml index 3351744611..ca33b8861c 100644 --- a/doc/src/sgml/client-auth.sgml +++ b/doc/src/sgml/client-auth.sgml @@ -388,6 +388,16 @@ hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable> </varlistentry> <varlistentry> + <term><literal>cert</></term> + <listitem> + <para> + Authenticate using SSL client certificates. See + <xref linkend="auth-cert"> for details. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><literal>pam</></term> <listitem> <para> @@ -1114,6 +1124,25 @@ ldapserver=ldap.example.net prefix="cn=" suffix="dc=example, dc=net" </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"> <title>PAM authentication</title> diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml index 3b507cd6c5..d2bd9af274 100644 --- a/doc/src/sgml/runtime.sgml +++ b/doc/src/sgml/runtime.sgml @@ -1674,11 +1674,9 @@ $ <userinput>kill -INT `head -1 /usr/local/pgsql/data/postmaster.pid`</userinput </para> <para> - <productname>PostgreSQL</> currently does not support authentication - using client certificates, since it cannot differentiate between - different users. As long as the user holds any certificate issued - by a trusted CA it will be accepted, regardless of what account the - user is trying to connect with. + You can use the authentication method <literal>cert</> to use the + client certificate for authenticating users. See + <xref linkend="auth-cert"> for details. </para> </sect2> diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index eaaa7a5262..104cddb4f9 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -113,6 +113,14 @@ ULONG(*__ldap_start_tls_sA) ( static int CheckLDAPAuth(Port *port); #endif /* USE_LDAP */ +/*---------------------------------------------------------------- + * Cert authentication + *---------------------------------------------------------------- + */ +#ifdef USE_SSL +static int CheckCertAuth(Port *port); +#endif + /*---------------------------------------------------------------- * Kerberos and GSSAPI GUCs @@ -431,6 +439,14 @@ ClientAuthentication(Port *port) #endif break; + case uaCert: +#ifdef USE_SSL + status = CheckCertAuth(port); +#else + Assert(false); +#endif + break; + case uaTrust: status = STATUS_OK; break; @@ -2120,3 +2136,28 @@ CheckLDAPAuth(Port *port) } #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 diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index f1338bc8c0..2b8ca9b1f4 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -859,6 +859,12 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline) #else unsupauth = "ldap"; #endif + else if (strcmp(token, "cert") == 0) +#ifdef USE_SSL + parsedline->auth_method = uaCert; +#else + unsupauth = "cert"; +#endif else { ereport(LOG, @@ -893,6 +899,17 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline) 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 */ 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 && parsedline->auth_method != uaKrb5 && parsedline->auth_method != uaGSS && - parsedline->auth_method != uaSSPI) - INVALID_AUTH_OPTION("map", "ident, krb5, gssapi and sspi"); + parsedline->auth_method != uaSSPI && + parsedline->auth_method != uaCert) + INVALID_AUTH_OPTION("map", "ident, krb5, gssapi, sspi and cert"); parsedline->usermap = pstrdup(c); } else if (strcmp(token, "clientcert") == 0) @@ -957,7 +975,18 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline) parsedline->clientcert = true; } 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; + } } 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"); } + + /* + * Enforce any parameters implied by other settings. + */ + if (parsedline->auth_method == uaCert) + { + parsedline->clientcert = true; + } return true; } diff --git a/src/backend/libpq/pg_hba.conf.sample b/src/backend/libpq/pg_hba.conf.sample index c84d955e1b..b50fa46c19 100644 --- a/src/backend/libpq/pg_hba.conf.sample +++ b/src/backend/libpq/pg_hba.conf.sample @@ -35,7 +35,7 @@ # an IP address and netmask in separate columns to specify the set of hosts. # # 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. # # OPTIONS are a set of options for the authentication in the format diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h index eecc50de1e..6672770340 100644 --- a/src/include/libpq/hba.h +++ b/src/include/libpq/hba.h @@ -26,7 +26,8 @@ typedef enum UserAuth uaGSS, uaSSPI, uaPAM, - uaLDAP + uaLDAP, + uaCert } UserAuth; typedef enum ConnType |