Обсуждение: Client certificate authentication
Attached patch implements client certificate authentication.
I kept this sitting in my tree without sending it in before the
commitfest because it is entirely dependent on the
not-yet-reviewed-and-applied patch for how to configure client
certificate requesting. But now that I learned how to do it right in
git, breaking it out was very easy :-) Good learning experience.
Anyway. Here it is. Builds on top of the "clientcert option for pg_hba"
patch already on the list.
//Magnus
*** a/doc/src/sgml/client-auth.sgml
--- b/doc/src/sgml/client-auth.sgml
***************
*** 388,393 **** hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable>
--- 388,403 ----
</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,1119 **** ldapserver=ldap.example.net prefix="cn=" suffix="dc=example, dc=net"
--- 1124,1148 ----
</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>
*** a/doc/src/sgml/runtime.sgml
--- b/doc/src/sgml/runtime.sgml
***************
*** 1674,1684 **** $ <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.
</para>
</sect2>
--- 1674,1682 ----
</para>
<para>
! 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>
*** a/src/backend/libpq/auth.c
--- b/src/backend/libpq/auth.c
***************
*** 110,115 **** ULONG(*__ldap_start_tls_sA) (
--- 110,123 ----
static int CheckLDAPAuth(Port *port);
#endif /* USE_LDAP */
+ /*----------------------------------------------------------------
+ * Cert authentication
+ *----------------------------------------------------------------
+ */
+ #ifdef USE_SSL
+ static int CheckCertAuth(Port *port);
+ #endif
+
/*----------------------------------------------------------------
* Kerberos and GSSAPI GUCs
***************
*** 420,425 **** ClientAuthentication(Port *port)
--- 428,441 ----
#endif
break;
+ case uaCert:
+ #ifdef USE_SSL
+ status = CheckCertAuth(port);
+ #else
+ Assert(false);
+ #endif
+ break;
+
case uaTrust:
status = STATUS_OK;
break;
***************
*** 2072,2074 **** CheckLDAPAuth(Port *port)
--- 2088,2115 ----
}
#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
*** a/src/backend/libpq/hba.c
--- b/src/backend/libpq/hba.c
***************
*** 859,864 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
--- 859,870 ----
#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,898 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
--- 899,915 ----
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,930 **** 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->usermap = pstrdup(c);
}
else if (strcmp(token, "clientcert") == 0)
--- 940,948 ----
if (parsedline->auth_method != uaIdent &&
parsedline->auth_method != uaKrb5 &&
parsedline->auth_method != uaGSS &&
! 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,963 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
--- 975,992 ----
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,1026 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
--- 1050,1063 ----
{
MANDATORY_AUTH_ARG(parsedline->ldapserver, "ldapserver", "ldap");
}
+
+ /*
+ * Enforce any parameters implied by other settings.
+ */
+ if (parsedline->auth_method == uaCert)
+ {
+ parsedline->clientcert = true;
+ }
return true;
}
*** a/src/backend/libpq/pg_hba.conf.sample
--- b/src/backend/libpq/pg_hba.conf.sample
***************
*** 35,41 ****
# 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
# in clear text; "md5" is preferred since it sends encrypted passwords.
#
# OPTIONS are a set of options for the authentication in the format
--- 35,41 ----
# 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", "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
*** a/src/include/libpq/hba.h
--- b/src/include/libpq/hba.h
***************
*** 26,32 **** typedef enum UserAuth
uaGSS,
uaSSPI,
uaPAM,
! uaLDAP
} UserAuth;
typedef enum ConnType
--- 26,33 ----
uaGSS,
uaSSPI,
uaPAM,
! uaLDAP,
! uaCert
} UserAuth;
typedef enum ConnType
On Thu, Nov 13, 2008 at 05:31, Magnus Hagander <magnus@hagander.net> wrote: > Attached patch implements client certificate authentication. > > I kept this sitting in my tree without sending it in before the > commitfest because it is entirely dependent on the > not-yet-reviewed-and-applied patch for how to configure client > certificate requesting. But now that I learned how to do it right in > git, breaking it out was very easy :-) Good learning experience. > > Anyway. Here it is. Builds on top of the "clientcert option for pg_hba" > patch already on the list. Patch looks good to me and works as described. Would cncert be a better auth_method name? As later we might have different types of ssl client cert authentication?? My only concern is there is no way to specify the USER_CERT_FILE for libpq. So if for example I have two users that I want to use cert authentication for I really have to have to users on the system (or i guess maybe you could fake HOME=... psql -U other_user). Or am I missing a way around this? (granted this might be a non-issue for now as you can use trust clientcert=1 in pg_hba.conf with your other patch?)
On 16 nov 2008, at 01.00, "Alex Hunsaker" <badalex@gmail.com> wrote: > On Thu, Nov 13, 2008 at 05:31, Magnus Hagander <magnus@hagander.net> > wrote: >> Attached patch implements client certificate authentication. >> >> I kept this sitting in my tree without sending it in before the >> commitfest because it is entirely dependent on the >> not-yet-reviewed-and-applied patch for how to configure client >> certificate requesting. But now that I learned how to do it right in >> git, breaking it out was very easy :-) Good learning experience. >> >> Anyway. Here it is. Builds on top of the "clientcert option for >> pg_hba" >> patch already on the list. > > Patch looks good to me and works as described. > > Would cncert be a better auth_method name? As later we might have > different types of ssl client cert authentication?? If/when I'd rather still call it cert, and use an authentication option to control which field is matched against. > My only concern is there is no way to specify the USER_CERT_FILE for > libpq. So if for example I have two users that I want to use cert > authentication for I really have to have to users on the system (or i > guess maybe you could fake HOME=... psql -U other_user). Or am I While not directly related to this patch, that is a very good point. We have PGSSLKEY but not PGSSLCERT. Could certainly be worth adding. > > missing a way around this? (granted this might be a non-issue for now > as you can use trust clientcert=1 in pg_hba.conf with your other > patch?) Yes, you can use that but the usecase is extremely limited. It only works if these are the *only* two users with certificates... -Magnus
Magnus Hagander escribió: > On 16 nov 2008, at 01.00, "Alex Hunsaker" <badalex@gmail.com> wrote: >> My only concern is there is no way to specify the USER_CERT_FILE for >> libpq. So if for example I have two users that I want to use cert >> authentication for I really have to have to users on the system (or i >> guess maybe you could fake HOME=... psql -U other_user). Or am I > > While not directly related to this patch, that is a very good point. We > have PGSSLKEY but not PGSSLCERT. Could certainly be worth adding. FWIW I think this was part of the patch submitted by Mark Woodward; see http://wiki.postgresql.org/wiki/CommitFest_2008-07, and http://archives.postgresql.org/message-id/20080801203157.GL4321@alvh.no-ip.org -- Alvaro Herrera http://www.CommandPrompt.com/ The PostgreSQL Company - Command Prompt, Inc.
Alvaro Herrera wrote: > Magnus Hagander escribió: >> On 16 nov 2008, at 01.00, "Alex Hunsaker" <badalex@gmail.com> wrote: > >>> My only concern is there is no way to specify the USER_CERT_FILE for >>> libpq. So if for example I have two users that I want to use cert >>> authentication for I really have to have to users on the system (or i >>> guess maybe you could fake HOME=... psql -U other_user). Or am I >> While not directly related to this patch, that is a very good point. We >> have PGSSLKEY but not PGSSLCERT. Could certainly be worth adding. > > FWIW I think this was part of the patch submitted by Mark Woodward; see > http://wiki.postgresql.org/wiki/CommitFest_2008-07, and > http://archives.postgresql.org/message-id/20080801203157.GL4321@alvh.no-ip.org Seems like it. I totally missed that one. As for the patch itself - do we really want to #ifdef all parameters out? There's no harm in accepting them for non-ssl connections (and ignoring them), and that might make life easier on third party stuff that fills in all parameters with their default values if they're not specified. Like we support sslmode even if we're compiled without SSL. And yes, sslkey and PGSSLKEY should be made the same thing, I think. //Magnus
On Mon, Nov 17, 2008 at 01:01, Magnus Hagander <magnus@hagander.net> wrote: > On 16 nov 2008, at 01.00, "Alex Hunsaker" <badalex@gmail.com> wrote: >> Would cncert be a better auth_method name? As later we might have >> different types of ssl client cert authentication?? > > If/when I'd rather still call it cert, and use an authentication option to > control which field is matched against. Makes sense to me. FYI I marked this as ready for commiter...
On Mon, Nov 17, 2008 at 05:31, Alvaro Herrera <alvherre@commandprompt.com> wrote: > Magnus Hagander escribió: >> On 16 nov 2008, at 01.00, "Alex Hunsaker" <badalex@gmail.com> wrote: > >>> My only concern is there is no way to specify the USER_CERT_FILE for >>> libpq. So if for example I have two users that I want to use cert >>> authentication for I really have to have to users on the system (or i >>> guess maybe you could fake HOME=... psql -U other_user). Or am I >> >> While not directly related to this patch, that is a very good point. We >> have PGSSLKEY but not PGSSLCERT. Could certainly be worth adding. > > FWIW I think this was part of the patch submitted by Mark Woodward; see > http://wiki.postgresql.org/wiki/CommitFest_2008-07, and > http://archives.postgresql.org/message-id/20080801203157.GL4321@alvh.no-ip.org Cool! I missed this one as well, too bad it does not look like it ever got resubmitted for this feast :( > -- > Alvaro Herrera http://www.CommandPrompt.com/ > The PostgreSQL Company - Command Prompt, Inc. >
Alex Hunsaker wrote: > On Mon, Nov 17, 2008 at 05:31, Alvaro Herrera > <alvherre@commandprompt.com> wrote: >> Magnus Hagander escribió: >>> On 16 nov 2008, at 01.00, "Alex Hunsaker" <badalex@gmail.com> wrote: >>>> My only concern is there is no way to specify the USER_CERT_FILE for >>>> libpq. So if for example I have two users that I want to use cert >>>> authentication for I really have to have to users on the system (or i >>>> guess maybe you could fake HOME=... psql -U other_user). Or am I >>> While not directly related to this patch, that is a very good point. We >>> have PGSSLKEY but not PGSSLCERT. Could certainly be worth adding. >> FWIW I think this was part of the patch submitted by Mark Woodward; see >> http://wiki.postgresql.org/wiki/CommitFest_2008-07, and >> http://archives.postgresql.org/message-id/20080801203157.GL4321@alvh.no-ip.org > > Cool! I missed this one as well, too bad it does not look like it ever > got resubmitted for this feast :( Actually, isn't that second mail the resubmission? That just didn't go up on the commitfest page properly? //Magnus