diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml index de473f201c..f10a93953e 100644 --- a/doc/src/sgml/client-auth.sgml +++ b/doc/src/sgml/client-auth.sgml @@ -1,4 +1,4 @@ - + Client Authentication @@ -387,6 +387,16 @@ hostnossl database user + + cert + + + Authenticate using SSL client certificates. See + for details. + + + + pam @@ -1114,6 +1124,25 @@ ldapserver=ldap.example.net prefix="cn=" suffix="dc=example, dc=net" + + Certificate authentication + + + Certificate + + + + 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 cn 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. + + + PAM authentication diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml index 1a862b5c4b..f40899e0d6 100644 --- a/doc/src/sgml/runtime.sgml +++ b/doc/src/sgml/runtime.sgml @@ -1,4 +1,4 @@ - + Operating System Environment @@ -1674,11 +1674,9 @@ $ kill -INT `head -1 /usr/local/pgsql/data/postmaster.pid` - 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 cert to use the + client certificate for authenticating users. See + for details. diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index dfa3ff2e9a..1d89e09682 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -8,7 +8,7 @@ * * * 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); #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 64f67818c9..2464c5f6f9 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -10,7 +10,7 @@ * * * 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; #else unsupauth = "ldap"; +#endif + else if (strcmp(token, "cert") == 0) +#ifdef USE_SSL + parsedline->auth_method = uaCert; +#else + unsupauth = "cert"; #endif else { @@ -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 cf5942a266..87d04640e4 100644 --- a/src/include/libpq/hba.h +++ b/src/include/libpq/hba.h @@ -4,7 +4,7 @@ * 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, uaSSPI, uaPAM, - uaLDAP + uaLDAP, + uaCert } UserAuth; typedef enum ConnType