提交 7356381e 编写于 作者: M Magnus Hagander

* make pg_hba authoption be a set of 0 or more name=value pairs

* make LDAP use this instead of the hacky previous method to specify
  the DN to bind as
* make all auth options behave the same when they are not compiled
  into the server
* rename "ident maps" to "user name maps", and support them for all
  auth methods that provide an external username

This makes a backwards incompatible change in the format of pg_hba.conf
for the ident, PAM and LDAP authentication methods.
上级 2675d043
<!-- $PostgreSQL: pgsql/doc/src/sgml/client-auth.sgml,v 1.108 2008/09/15 12:41:54 mha Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/client-auth.sgml,v 1.109 2008/10/23 13:31:09 mha Exp $ -->
<chapter id="client-authentication">
<title>Client Authentication</title>
......@@ -96,13 +96,13 @@
<para>
A record can have one of the seven formats
<synopsis>
local <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-option</replaceable></optional>
host <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>CIDR-address</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-option</replaceable></optional>
hostssl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>CIDR-address</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-option</replaceable></optional>
hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>CIDR-address</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-option</replaceable></optional>
host <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-option</replaceable></optional>
hostssl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-option</replaceable></optional>
hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-option</replaceable></optional>
local <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional>
host <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>CIDR-address</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional>
hostssl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>CIDR-address</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional>
hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>CIDR-address</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional>
host <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional>
hostssl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional>
hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional>
</synopsis>
The meaning of the fields is as follows:
......@@ -422,11 +422,13 @@ hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable>
</varlistentry>
<varlistentry>
<term><replaceable>auth-option</replaceable></term>
<term><replaceable>auth-options</replaceable></term>
<listitem>
<para>
The meaning of this optional field depends on the chosen
authentication method. Details appear below.
This field contains zero or more name-value pairs with
extra options passed to this authentication method. Details
about which options are available for which authentication
method appear below.
</para>
</listitem>
</varlistentry>
......@@ -534,7 +536,7 @@ host all all 0.0.0.0/0 krb5
# "omicron" that says "bryanh" is allowed to connect as "guest1".
#
# TYPE DATABASE USER CIDR-ADDRESS METHOD
host all all 192.168.0.0/16 ident omicron
host all all 192.168.0.0/16 ident map=omicron
# If these are the only three lines for local connections, they will
# allow local users to connect only to their own databases (databases
......@@ -557,6 +559,92 @@ local db1,db2,@demodbs all md5
</example>
</sect1>
<sect1 id="auth-username-maps">
<title>Username maps</title>
<indexterm zone="auth-username-maps">
<primary>Username maps</primary>
</indexterm>
<para>
When using an external authentication system like Ident or GSSAPI,
the name of the operating system user that initiated the connection may
not be the same as the database user he is requesting to connect as.
In this case, a user name map can be applied to map the operating system
username to a database user, using the <filename>pg_ident.conf</filename>
file. In order to use username mapping, specify
<literal>map</literal>=<replaceable>map-name</replaceable>
in the options field in <filename>pg_hba.conf</filename>. This option is
supported for all authentication methods that receive external usernames.
Since the <filename>pg_ident.conf</filename> file can contain multiple maps,
the name of the map to be used is specified in the
<replaceable>map-name</replaceable> parameter in <filename>pg_hba.conf</filename>
to indicate which map to use for each individual connection.
</para>
<para>
Ident maps are defined in the ident map file, which by default is named
<filename>pg_ident.conf</><indexterm><primary>pg_ident.conf</primary></indexterm>
and is stored in the
cluster's data directory. (It is possible to place the map file
elsewhere, however; see the <xref linkend="guc-ident-file">
configuration parameter.)
The ident map file contains lines of the general form:
<synopsis>
<replaceable>map-name</> <replaceable>system-username</> <replaceable>database-username</>
</synopsis>
Comments and whitespace are handled in the same way as in
<filename>pg_hba.conf</>. The
<replaceable>map-name</> is an arbitrary name that will be used to
refer to this mapping in <filename>pg_hba.conf</filename>. The other
two fields specify which operating system user is allowed to connect
as which database user. The same <replaceable>map-name</> can be
used repeatedly to specify more user-mappings within a single map.
There is no restriction regarding how many database users a given
operating system user can correspond to, nor vice versa.
</para>
<para>
The <filename>pg_ident.conf</filename> file is read on start-up and
when the main server process receives a
<systemitem>SIGHUP</systemitem><indexterm><primary>SIGHUP</primary></indexterm>
signal. If you edit the file on an
active system, you will need to signal the server
(using <literal>pg_ctl reload</> or <literal>kill -HUP</>) to make it
re-read the file.
</para>
<para>
A <filename>pg_ident.conf</filename> file that could be used in
conjunction with the <filename>pg_hba.conf</> file in <xref
linkend="example-pg-hba.conf"> is shown in <xref
linkend="example-pg-ident.conf">. In this example setup, anyone
logged in to a machine on the 192.168 network that does not have the
Unix user name <literal>bryanh</>, <literal>ann</>, or
<literal>robert</> would not be granted access. Unix user
<literal>robert</> would only be allowed access when he tries to
connect as <productname>PostgreSQL</> user <literal>bob</>, not
as <literal>robert</> or anyone else. <literal>ann</> would
only be allowed to connect as <literal>ann</>. User
<literal>bryanh</> would be allowed to connect as either
<literal>bryanh</> himself or as <literal>guest1</>.
</para>
<example id="example-pg-ident.conf">
<title>An example <filename>pg_ident.conf</> file</title>
<programlisting>
# MAPNAME IDENT-USERNAME PG-USERNAME
omicron bryanh bryanh
omicron ann ann
# bob has user name robert on these machines
omicron robert bob
# bryanh can also connect as guest1
omicron bryanh guest1
</programlisting>
</example>
</sect1>
<sect1 id="auth-methods">
<title>Authentication methods</title>
<para>
......@@ -686,6 +774,20 @@ local db1,db2,@demodbs all md5
see <xref linkend="installation"> for more information.
</para>
<para>
The following configuration options are supported for <productname>GSSAPI</productname>:
<variablelist>
<varlistentry>
<term>map</term>
<listitem>
<para>
Allows for mapping between system and database usernames. See
<xref linkend="auth-username-maps"> for details.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</sect2>
<sect2 id="sspi-auth">
......@@ -713,6 +815,20 @@ local db1,db2,@demodbs all md5
for details.
</para>
<para>
The following configuration options are supported for <productname>SSPI</productname>:
<variablelist>
<varlistentry>
<term>map</term>
<listitem>
<para>
Allows for mapping between system and database usernames. See
<xref linkend="auth-username-maps"> for details.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</sect2>
<sect2 id="kerberos-auth">
......@@ -846,6 +962,21 @@ local db1,db2,@demodbs all md5
depending on the connection type.
</para>
<para>
The following configuration options are supported for <productname>GSSAPI</productname>:
<variablelist>
<varlistentry>
<term>map</term>
<listitem>
<para>
Allows for mapping between system and database usernames. See
<xref linkend="auth-username-maps"> for details.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<sect3>
<title>Ident Authentication over TCP/IP</title>
......@@ -918,83 +1049,6 @@ local db1,db2,@demodbs all md5
</para>
</sect3>
<sect3 id="auth-ident-maps">
<title>Ident Maps</title>
<para>
When using ident-based authentication, after having determined the
name of the operating system user that initiated the connection,
<productname>PostgreSQL</productname> checks whether that user is
allowed to connect as the database user he is requesting to connect
as. This is controlled by the ident map argument that follows the
<literal>ident</> key word in the <filename>pg_hba.conf</filename>
file. If an ident map is not specified, the database user will be
checked with the same name as the operating system user. Other maps
must be created manually.
</para>
<para>
Ident maps are defined in the ident map file, which by default is named
<filename>pg_ident.conf</><indexterm><primary>pg_ident.conf</primary></indexterm>
and is stored in the
cluster's data directory. (It is possible to place the map file
elsewhere, however; see the <xref linkend="guc-ident-file">
configuration parameter.)
The ident map file contains lines of the general form:
<synopsis>
<replaceable>map-name</> <replaceable>ident-username</> <replaceable>database-username</>
</synopsis>
Comments and whitespace are handled in the same way as in
<filename>pg_hba.conf</>. The
<replaceable>map-name</> is an arbitrary name that will be used to
refer to this mapping in <filename>pg_hba.conf</filename>. The other
two fields specify which operating system user is allowed to connect
as which database user. The same <replaceable>map-name</> can be
used repeatedly to specify more user-mappings within a single map.
There is no restriction regarding how many database users a given
operating system user can correspond to, nor vice versa.
</para>
<para>
The <filename>pg_ident.conf</filename> file is read on start-up and
when the main server process receives a
<systemitem>SIGHUP</systemitem><indexterm><primary>SIGHUP</primary></indexterm>
signal. If you edit the file on an
active system, you will need to signal the server
(using <literal>pg_ctl reload</> or <literal>kill -HUP</>) to make it
re-read the file.
</para>
<para>
A <filename>pg_ident.conf</filename> file that could be used in
conjunction with the <filename>pg_hba.conf</> file in <xref
linkend="example-pg-hba.conf"> is shown in <xref
linkend="example-pg-ident.conf">. In this example setup, anyone
logged in to a machine on the 192.168 network that does not have the
Unix user name <literal>bryanh</>, <literal>ann</>, or
<literal>robert</> would not be granted access. Unix user
<literal>robert</> would only be allowed access when he tries to
connect as <productname>PostgreSQL</> user <literal>bob</>, not
as <literal>robert</> or anyone else. <literal>ann</> would
only be allowed to connect as <literal>ann</>. User
<literal>bryanh</> would be allowed to connect as either
<literal>bryanh</> himself or as <literal>guest1</>.
</para>
<example id="example-pg-ident.conf">
<title>An example <filename>pg_ident.conf</> file</title>
<programlisting>
# MAPNAME IDENT-USERNAME PG-USERNAME
omicron bryanh bryanh
omicron ann ann
# bob has user name robert on these machines
omicron robert bob
# bryanh can also connect as guest1
omicron bryanh guest1
</programlisting>
</example>
</sect3>
</sect2>
<sect2 id="auth-ldap">
......@@ -1007,49 +1061,84 @@ omicron bryanh guest1
<para>
This authentication method operates similarly to
<literal>password</literal> except that it uses LDAP
as the authentication method. LDAP is used only to validate
as the password verification method. LDAP is used only to validate
the user name/password pairs. Therefore the user must already
exist in the database before LDAP can be used for
authentication. The server and parameters used are specified
after the <literal>ldap</> key word in the file
<filename>pg_hba.conf</filename>. The format of this parameter is:
<synopsis>
ldap[<replaceable>s</>]://<replaceable>servername</>[:<replaceable>port</>]/<replaceable>base dn</replaceable>[;<replaceable>prefix</>[;<replaceable>suffix</>]]
</synopsis>
Commas are used to specify multiple items in an <literal>ldap</>
component. However, because unquoted commas are treated as item
separators in <filename>pg_hba.conf</filename>, it is wise to
double-quote the <literal>ldap</> URL to preserve any commas present,
e.g.:
<synopsis>
"ldap://ldap.example.net/dc=example,dc=net;EXAMPLE\"
</synopsis>
authentication.
</para>
<para>
The server will bind to the distinguished name constructed as
<replaceable>prefix</> <replaceable>username</> <replaceable>suffix</>.
before the bind. Typically, the prefix parameter is used to specify
<replaceable>cn=</>, or <replaceable>DOMAIN\</> in an Active
Directory environment, and suffix is used to specify the remaining part
of the DN in a non-Active Directory environment.
</para>
<para>
The following configuration options are supported for LDAP:
<variablelist>
<varlistentry>
<term>ldapserver</term>
<listitem>
<para>
Name or IP of LDAP server to connect to.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>ldapprefix</term>
<listitem>
<para>
If <literal>ldaps</> is specified instead of <literal>ldap</>,
TLS encryption will be enabled for the connection. Note that this
will encrypt only the connection between the PostgreSQL server
and the LDAP server. The connection between the client and the
PostgreSQL server is not affected by this setting. To make use of
TLS encryption, you might need to configure the LDAP library prior
to configuring PostgreSQL. Note that encrypted LDAP is available only
if the platform's LDAP library supports it.
String to prepend to the username when building the base DN to
bind as.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>ldapsuffix</term>
<listitem>
<para>
If no port is specified, the default port as configured in the
LDAP library will be used.
String to append to the username when building the base DN to
bind as.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>ldapport</term>
<listitem>
<para>
The server will bind to the distinguished name specified as
<replaceable>base dn</> using the user name supplied by the client.
If <replaceable>prefix</> and <replaceable>suffix</> is
specified, it will be prepended and appended to the user name
before the bind. Typically, the prefix parameter is used to specify
<replaceable>cn=</>, or <replaceable>DOMAIN\</> in an Active
Directory environment.
Port number on LDAP server to connect to. If no port is specified,
the default port in the LDAP library will be used.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>ldaptls</term>
<listitem>
<para>
Set to 1 to make the connection between PostgreSQL and the
LDAP server use TLS encryption. Note that this only encrypts
the traffic to the LDAP server - the connection to the client
may still be unencrypted unless TLS is used there as well.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<note>
<para>
Since LDAP often uses commas and spaces to separate the different
parts of a DN, it is advised to always use double-quoted parameter
values when configuring LDAP options, such as:
</para>
</note>
<synopsis>
ldapserver=ldap.example.net prefix="cn=" suffix="dc=example, dc=net"
</synopsis>
</sect2>
<sect2 id="auth-pam">
......@@ -1063,9 +1152,7 @@ ldap[<replaceable>s</>]://<replaceable>servername</>[:<replaceable>port</>]/<rep
This authentication method operates similarly to
<literal>password</literal> except that it uses PAM (Pluggable
Authentication Modules) as the authentication mechanism. The
default PAM service name is <literal>postgresql</literal>. You can
optionally supply your own service name after the <literal>pam</>
key word in the file <filename>pg_hba.conf</filename>.
default PAM service name is <literal>postgresql</literal>.
PAM is used only to validate user name/password pairs.
Therefore the user must already exist in the database before PAM
can be used for authentication. For more information about
......@@ -1075,6 +1162,20 @@ ldap[<replaceable>s</>]://<replaceable>servername</>[:<replaceable>port</>]/<rep
<systemitem class="osname">Solaris</> PAM Page</ulink>.
</para>
<para>
The following configuration options are supported for PAM:
<variablelist>
<varlistentry>
<term>pamservice</term>
<listitem>
<para>
PAM service name.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<note>
<para>
If PAM is set up to read <filename>/etc/shadow</>, authentication
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.168 2008/09/15 12:32:56 mha Exp $
* $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.169 2008/10/23 13:31:10 mha Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -126,9 +126,8 @@ char *pg_krb_realm = NULL;
* MIT Kerberos authentication system - protocol version 5
*----------------------------------------------------------------
*/
static int pg_krb5_recvauth(Port *port);
#ifdef KRB5
static int pg_krb5_recvauth(Port *port);
#include <krb5.h>
/* Some old versions of Kerberos do not include <com_err.h> in <krb5.h> */
......@@ -150,14 +149,14 @@ static krb5_principal pg_krb5_server;
* GSSAPI Authentication
*----------------------------------------------------------------
*/
static int pg_GSS_recvauth(Port *port);
#ifdef ENABLE_GSS
#if defined(HAVE_GSSAPI_H)
#include <gssapi.h>
#else
#include <gssapi/gssapi.h>
#endif
static int pg_GSS_recvauth(Port *port);
#endif /* ENABLE_GSS */
......@@ -165,12 +164,11 @@ static int pg_GSS_recvauth(Port *port);
* SSPI Authentication
*----------------------------------------------------------------
*/
static int pg_SSPI_recvauth(Port *port);
#ifdef ENABLE_SSPI
typedef SECURITY_STATUS
(WINAPI * QUERY_SECURITY_CONTEXT_TOKEN_FN) (
PCtxtHandle, void **);
static int pg_SSPI_recvauth(Port *port);
#endif
......@@ -236,16 +234,12 @@ auth_failed(Port *port, int status)
case uaPassword:
errstr = gettext_noop("password authentication failed for user \"%s\"");
break;
#ifdef USE_PAM
case uaPAM:
errstr = gettext_noop("PAM authentication failed for user \"%s\"");
break;
#endif /* USE_PAM */
#ifdef USE_LDAP
case uaLDAP:
errstr = gettext_noop("LDAP authentication failed for user \"%s\"");
break;
#endif /* USE_LDAP */
default:
errstr = gettext_noop("authentication failed for user \"%s\": invalid authentication method");
break;
......@@ -316,18 +310,30 @@ ClientAuthentication(Port *port)
}
case uaKrb5:
#ifdef KRB5
sendAuthRequest(port, AUTH_REQ_KRB5);
status = pg_krb5_recvauth(port);
#else
Assert(false);
#endif
break;
case uaGSS:
#ifdef ENABLE_GSS
sendAuthRequest(port, AUTH_REQ_GSS);
status = pg_GSS_recvauth(port);
#else
Assert(false);
#endif
break;
case uaSSPI:
#ifdef ENABLE_SSPI
sendAuthRequest(port, AUTH_REQ_SSPI);
status = pg_SSPI_recvauth(port);
#else
Assert(false);
#endif
break;
case uaIdent:
......@@ -377,18 +383,22 @@ ClientAuthentication(Port *port)
status = recv_and_check_password_packet(port);
break;
#ifdef USE_PAM
case uaPAM:
#ifdef USE_PAM
pam_port_cludge = port;
status = CheckPAMAuth(port, port->user_name, "");
break;
#else
Assert(false);
#endif /* USE_PAM */
break;
#ifdef USE_LDAP
case uaLDAP:
#ifdef USE_LDAP
status = CheckLDAPAuth(port);
break;
#else
Assert(false);
#endif
break;
case uaTrust:
status = STATUS_OK;
......@@ -713,19 +723,8 @@ pg_krb5_recvauth(Port *port)
return STATUS_ERROR;
}
if (pg_krb_caseins_users)
ret = pg_strncasecmp(port->user_name, kusername, SM_DATABASE_USER);
else
ret = strncmp(port->user_name, kusername, SM_DATABASE_USER);
if (ret)
{
ereport(LOG,
(errmsg("unexpected Kerberos user name received from client (received \"%s\", expected \"%s\")",
port->user_name, kusername)));
ret = STATUS_ERROR;
}
else
ret = STATUS_OK;
ret = check_usermap(port->hba->usermap, port->user_name, kusername,
pg_krb_caseins_users);
krb5_free_ticket(pg_krb5_context, ticket);
krb5_auth_con_free(pg_krb5_context, auth_context);
......@@ -733,16 +732,6 @@ pg_krb5_recvauth(Port *port)
return ret;
}
#else
static int
pg_krb5_recvauth(Port *port)
{
ereport(LOG,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Kerberos 5 not implemented on this server")));
return STATUS_ERROR;
}
#endif /* KRB5 */
......@@ -1020,38 +1009,13 @@ pg_GSS_recvauth(Port *port)
return STATUS_ERROR;
}
if (pg_krb_caseins_users)
ret = pg_strcasecmp(port->user_name, gbuf.value);
else
ret = strcmp(port->user_name, gbuf.value);
if (ret)
{
/* GSS name and PGUSER are not equivalent */
elog(DEBUG2,
"provided username (%s) and GSSAPI username (%s) don't match",
port->user_name, (char *) gbuf.value);
gss_release_buffer(&lmin_s, &gbuf);
return STATUS_ERROR;
}
ret = check_usermap(port->hba->usermap, port->user_name, gbuf.value,
pg_krb_caseins_users);
gss_release_buffer(&lmin_s, &gbuf);
return STATUS_OK;
}
#else /* no ENABLE_GSS */
static int
pg_GSS_recvauth(Port *port)
{
ereport(LOG,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("GSSAPI not implemented on this server")));
return STATUS_ERROR;
}
#endif /* ENABLE_GSS */
......@@ -1328,30 +1292,8 @@ pg_SSPI_recvauth(Port *port)
* We have the username (without domain/realm) in accountname, compare to
* the supplied value. In SSPI, always compare case insensitive.
*/
if (pg_strcasecmp(port->user_name, accountname))
{
/* GSS name and PGUSER are not equivalent */
elog(DEBUG2,
"provided username (%s) and SSPI username (%s) don't match",
port->user_name, accountname);
return STATUS_ERROR;
}
return STATUS_OK;
return check_usermap(port->hba->usermap, port->user_name, accountname, true);
}
#else /* no ENABLE_SSPI */
static int
pg_SSPI_recvauth(Port *port)
{
ereport(LOG,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("SSPI not implemented on this server")));
return STATUS_ERROR;
}
#endif /* ENABLE_SSPI */
......@@ -1795,14 +1737,7 @@ authident(hbaPort *port)
return STATUS_ERROR;
}
ereport(DEBUG2,
(errmsg("Ident protocol identifies remote user as \"%s\"",
ident_user)));
if (check_ident_usermap(port->hba->usermap, port->user_name, ident_user))
return STATUS_OK;
else
return STATUS_ERROR;
return check_usermap(port->hba->usermap, port->user_name, ident_user, false);
}
......@@ -1913,8 +1848,8 @@ CheckPAMAuth(Port *port, char *user, char *password)
* not allocated */
/* Optionally, one can set the service name in pg_hba.conf */
if (port->hba->auth_arg && port->hba->auth_arg[0] != '\0')
retval = pam_start(port->hba->auth_arg, "pgsql@",
if (port->hba->pamservice && port->hba->pamservice[0] != '\0')
retval = pam_start(port->hba->pamservice, "pgsql@",
&pam_passw_conv, &pamh);
else
retval = pam_start(PGSQL_PAM_SERVICE, "pgsql@",
......@@ -2000,76 +1935,20 @@ static int
CheckLDAPAuth(Port *port)
{
char *passwd;
char server[128];
char basedn[128];
char prefix[128];
char suffix[128];
LDAP *ldap;
bool ssl = false;
int r;
int ldapversion = LDAP_VERSION3;
int ldapport = LDAP_PORT;
char fulluser[NAMEDATALEN + 256 + 1];
if (!port->hba->auth_arg || port->hba->auth_arg[0] == '\0')
if (!port->hba->ldapserver|| port->hba->ldapserver[0] == '\0')
{
ereport(LOG,
(errmsg("LDAP configuration URL not specified")));
(errmsg("LDAP server not specified")));
return STATUS_ERROR;
}
/*
* Crack the LDAP url. We do a very trivial parse:
*
* ldap[s]://<server>[:<port>]/<basedn>[;prefix[;suffix]]
*
* This code originally used "%127s" for the suffix, but that doesn't
* work for embedded whitespace. We know that tokens formed by
* hba.c won't include newlines, so we can use a "not newline" scanset
* instead.
*/
server[0] = '\0';
basedn[0] = '\0';
prefix[0] = '\0';
suffix[0] = '\0';
/* ldap, including port number */
r = sscanf(port->hba->auth_arg,
"ldap://%127[^:]:%d/%127[^;];%127[^;];%127[^\n]",
server, &ldapport, basedn, prefix, suffix);
if (r < 3)
{
/* ldaps, including port number */
r = sscanf(port->hba->auth_arg,
"ldaps://%127[^:]:%d/%127[^;];%127[^;];%127[^\n]",
server, &ldapport, basedn, prefix, suffix);
if (r >= 3)
ssl = true;
}
if (r < 3)
{
/* ldap, no port number */
r = sscanf(port->hba->auth_arg,
"ldap://%127[^/]/%127[^;];%127[^;];%127[^\n]",
server, basedn, prefix, suffix);
}
if (r < 2)
{
/* ldaps, no port number */
r = sscanf(port->hba->auth_arg,
"ldaps://%127[^/]/%127[^;];%127[^;];%127[^\n]",
server, basedn, prefix, suffix);
if (r >= 2)
ssl = true;
}
if (r < 2)
{
ereport(LOG,
(errmsg("invalid LDAP URL: \"%s\"",
port->hba->auth_arg)));
return STATUS_ERROR;
}
if (port->hba->ldapport == 0)
port->hba->ldapport = LDAP_PORT;
sendAuthRequest(port, AUTH_REQ_PASSWORD);
......@@ -2077,7 +1956,7 @@ CheckLDAPAuth(Port *port)
if (passwd == NULL)
return STATUS_EOF; /* client wouldn't send password */
ldap = ldap_init(server, ldapport);
ldap = ldap_init(port->hba->ldapserver, port->hba->ldapport);
if (!ldap)
{
#ifndef WIN32
......@@ -2100,7 +1979,7 @@ CheckLDAPAuth(Port *port)
return STATUS_ERROR;
}
if (ssl)
if (port->hba->ldaptls)
{
#ifndef WIN32
if ((r = ldap_start_tls_s(ldap, NULL, NULL)) != LDAP_SUCCESS)
......@@ -2155,7 +2034,9 @@ CheckLDAPAuth(Port *port)
}
snprintf(fulluser, sizeof(fulluser), "%s%s%s",
prefix, port->user_name, suffix);
port->hba->ldapprefix ? port->hba->ldapprefix : "",
port->user_name,
port->hba->ldapsuffix ? port->hba->ldapsuffix : "");
fulluser[sizeof(fulluser) - 1] = '\0';
r = ldap_simple_bind_s(ldap, fulluser, passwd);
......@@ -2165,7 +2046,7 @@ CheckLDAPAuth(Port *port)
{
ereport(LOG,
(errmsg("LDAP login failed for user \"%s\" on server \"%s\": error code %d",
fulluser, server, r)));
fulluser, port->hba->ldapserver, r)));
return STATUS_ERROR;
}
......
......@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.168 2008/09/15 20:55:04 mha Exp $
* $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.169 2008/10/23 13:31:10 mha Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -564,6 +564,44 @@ check_db(const char *dbname, const char *role, char *param_str)
}
/*
* Macros used to check and report on invalid configuration options.
* INVALID_AUTH_OPTION = reports when an option is specified for a method where it's
* not supported.
* REQUIRE_AUTH_OPTION = same as INVALID_AUTH_OPTION, except it also checks if the
* method is actually the one specified. Used as a shortcut when
* the option is only valid for one authentication method.
* MANDATORY_AUTH_ARG = check if a required option is set for an authentication method,
* reporting error if it's not.
*/
#define INVALID_AUTH_OPTION(optname, validmethods) do {\
ereport(LOG, \
(errcode(ERRCODE_CONFIG_FILE_ERROR), \
errmsg("authentication option '%s' is only valid for authentication methods '%s'", \
optname, validmethods), \
errcontext("line %d of configuration file \"%s\"", \
line_num, HbaFileName))); \
goto hba_other_error; \
} while (0);
#define REQUIRE_AUTH_OPTION(methodval, optname, validmethods) do {\
if (parsedline->auth_method != methodval) \
INVALID_AUTH_OPTION("ldaptls", "ldap"); \
} while (0);
#define MANDATORY_AUTH_ARG(argvar, argname, authname) do {\
if (argvar == NULL) {\
ereport(LOG, \
(errcode(ERRCODE_CONFIG_FILE_ERROR), \
errmsg("authentication method '%s' requires argument '%s' to be set", \
authname, argname), \
errcontext("line %d of configuration file \"%s\"", \
line_num, HbaFileName))); \
goto hba_other_error; \
} \
} while (0);
/*
* Parse one line in the hba config file and store the result in
* a HbaLine structure.
......@@ -801,39 +839,103 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
goto hba_other_error;
}
/* Get the authentication argument token, if any */
line_item = lnext(line_item);
if (line_item)
/* Parse remaining arguments */
while ((line_item = lnext(line_item)) != NULL)
{
char *c;
token = lfirst(line_item);
parsedline->auth_arg= pstrdup(token);
}
c = strchr(token, '=');
if (c == NULL)
{
/*
* Backwards compatible format of ident authentication - support "naked" ident map
* name, as well as "sameuser"/"samerole"
* Got something that's not a name=value pair.
*
* XXX: attempt to do some backwards compatible parsing here?
*/
if (parsedline->auth_method == uaIdent)
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("authentication option not in name=value format: %s", token),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
goto hba_other_error;
}
else
{
if (parsedline->auth_arg && strlen(parsedline->auth_arg))
*c++ = '\0'; /* token now holds "name", c holds "value" */
if (strcmp(token, "map") == 0)
{
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, "pamservice") == 0)
{
REQUIRE_AUTH_OPTION(uaPAM, "pamservice", "pam");
parsedline->pamservice = pstrdup(c);
}
else if (strcmp(token, "ldaptls") == 0)
{
REQUIRE_AUTH_OPTION(uaLDAP, "ldaptls", "ldap");
if (strcmp(c, "1") == 0)
parsedline->ldaptls = true;
else
parsedline->ldaptls = false;
}
else if (strcmp(token, "ldapserver") == 0)
{
REQUIRE_AUTH_OPTION(uaLDAP, "ldapserver", "ldap");
parsedline->ldapserver = pstrdup(c);
}
else if (strcmp(token, "ldapport") == 0)
{
REQUIRE_AUTH_OPTION(uaLDAP, "ldapport", "ldap");
parsedline->ldapport = atoi(c);
if (parsedline->ldapport == 0)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid ldap port '%s'", c),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
goto hba_other_error;
}
}
else if (strcmp(token, "ldapprefix") == 0)
{
if (strcmp(parsedline->auth_arg, "sameuser\n") == 0 ||
strcmp(parsedline->auth_arg, "samerole\n") == 0)
REQUIRE_AUTH_OPTION(uaLDAP, "ldapprefix", "ldap");
parsedline->ldapprefix = pstrdup(c);
}
else if (strcmp(token, "ldapsuffix") == 0)
{
/* This is now the default */
pfree(parsedline->auth_arg);
parsedline->auth_arg = NULL;
parsedline->usermap = NULL;
REQUIRE_AUTH_OPTION(uaLDAP, "ldapsuffix", "ldap");
parsedline->ldapsuffix = pstrdup(c);
}
else
{
/* Specific ident map specified */
parsedline->usermap = parsedline->auth_arg;
parsedline->auth_arg = NULL;
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("unknown authentication option name '%s'", token),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
goto hba_other_error;
}
}
}
/*
* Check if the selected authentication method has any mandatory arguments that
* are not set.
*/
if (parsedline->auth_method == uaLDAP)
{
MANDATORY_AUTH_ARG(parsedline->ldapserver, "ldapserver", "ldap");
}
return true;
hba_syntax:
......@@ -1018,8 +1120,14 @@ free_hba_record(HbaLine *record)
pfree(record->database);
if (record->role)
pfree(record->role);
if (record->auth_arg)
pfree(record->auth_arg);
if (record->pamservice)
pfree(record->pamservice);
if (record->ldapserver)
pfree(record->ldapserver);
if (record->ldapprefix)
pfree(record->ldapprefix);
if (record->ldapsuffix)
pfree(record->ldapsuffix);
}
/*
......@@ -1150,7 +1258,7 @@ read_pg_database_line(FILE *fp, char *dbname, Oid *dboid,
static void
parse_ident_usermap(List *line, int line_number, const char *usermap_name,
const char *pg_role, const char *ident_user,
bool *found_p, bool *error_p)
bool case_insensitive, bool *found_p, bool *error_p)
{
ListCell *line_item;
char *token;
......@@ -1183,10 +1291,20 @@ parse_ident_usermap(List *line, int line_number, const char *usermap_name,
file_pgrole = token;
/* Match? */
if (case_insensitive)
{
if (strcmp(file_map, usermap_name) == 0 &&
pg_strcasecmp(file_pgrole, pg_role) == 0 &&
pg_strcasecmp(file_ident_user, ident_user) == 0)
*found_p = true;
}
else
{
if (strcmp(file_map, usermap_name) == 0 &&
strcmp(file_pgrole, pg_role) == 0 &&
strcmp(file_ident_user, ident_user) == 0)
*found_p = true;
}
return;
......@@ -1210,22 +1328,32 @@ ident_syntax:
* file. That's an implied map where "pgrole" must be identical to
* "ident_user" in order to be authorized.
*
* Iff authorized, return true.
* Iff authorized, return STATUS_OK, otherwise return STATUS_ERROR.
*/
bool
check_ident_usermap(const char *usermap_name,
int
check_usermap(const char *usermap_name,
const char *pg_role,
const char *ident_user)
const char *auth_user,
bool case_insensitive)
{
bool found_entry = false,
error = false;
if (usermap_name == NULL || usermap_name[0] == '\0')
{
if (strcmp(pg_role, ident_user) == 0)
found_entry = true;
else
found_entry = false;
if (case_insensitive)
{
if (pg_strcasecmp(pg_role, auth_user) == 0)
return STATUS_OK;
}
else {
if (strcmp(pg_role, auth_user) == 0)
return STATUS_OK;
}
ereport(LOG,
(errmsg("provided username (%s) and authenticated username (%s) don't match",
auth_user, pg_role)));
return STATUS_ERROR;
}
else
{
......@@ -1235,13 +1363,20 @@ check_ident_usermap(const char *usermap_name,
forboth(line_cell, ident_lines, num_cell, ident_line_nums)
{
parse_ident_usermap(lfirst(line_cell), lfirst_int(num_cell),
usermap_name, pg_role, ident_user,
usermap_name, pg_role, auth_user, case_insensitive,
&found_entry, &error);
if (found_entry || error)
break;
}
}
return found_entry;
if (!found_entry && !error)
{
ereport(LOG,
(errmsg("no match in usermap for user '%s' authenticated as '%s'",
pg_role, auth_user),
errcontext("usermap '%s'", usermap_name)));
}
return found_entry?STATUS_OK:STATUS_ERROR;
}
......
......@@ -9,10 +9,10 @@
# are authenticated, which PostgreSQL user names they can use, which
# databases they can access. Records take one of these forms:
#
# local DATABASE USER METHOD [OPTION]
# host DATABASE USER CIDR-ADDRESS METHOD [OPTION]
# hostssl DATABASE USER CIDR-ADDRESS METHOD [OPTION]
# hostnossl DATABASE USER CIDR-ADDRESS METHOD [OPTION]
# local DATABASE USER METHOD [OPTIONS]
# host DATABASE USER CIDR-ADDRESS METHOD [OPTIONS]
# hostssl DATABASE USER CIDR-ADDRESS METHOD [OPTIONS]
# hostnossl DATABASE USER CIDR-ADDRESS METHOD [OPTIONS]
#
# (The uppercase items must be replaced by actual values.)
#
......@@ -38,7 +38,10 @@
# "krb5", "ident", "pam" or "ldap". Note that "password" sends passwords
# in clear text; "md5" is preferred since it sends encrypted passwords.
#
# OPTION is the ident map or the name of the PAM service, depending on METHOD.
# OPTIONS are a set of options for the authentication in the format
# NAME=VALUE. The available options depend on the different authentication
# methods - refer to the "Client Authentication" section in the documentation
# for a list of which options are available for which authentication methods.
#
# Database and user names containing spaces, commas, quotes and other special
# characters must be quoted. Quoting one of the keywords "all", "sameuser" or
......
......@@ -5,18 +5,18 @@
# Authentication" for a complete description. A short synopsis
# follows.
#
# This file controls PostgreSQL ident-based authentication. It maps
# ident user names (typically Unix user names) to their corresponding
# This file controls PostgreSQL username mapping. It maps
# external user names to their corresponding
# PostgreSQL user names. Records are of the form:
#
# MAPNAME IDENT-USERNAME PG-USERNAME
# MAPNAME SYSTEM-USERNAME PG-USERNAME
#
# (The uppercase quantities must be replaced by actual values.)
#
# MAPNAME is the (otherwise freely chosen) map name that was used in
# pg_hba.conf. IDENT-USERNAME is the detected user name of the
# pg_hba.conf. SYSTEM-USERNAME is the detected user name of the
# client. PG-USERNAME is the requested PostgreSQL user name. The
# existence of a record specifies that IDENT-USERNAME may connect as
# existence of a record specifies that SYSTEM-USERNAME may connect as
# PG-USERNAME. Multiple maps may be specified in this file and used
# by pg_hba.conf.
#
......@@ -28,8 +28,8 @@
# Put your actual configuration here
# ----------------------------------
#
# No map names are defined in the default configuration. If all ident
# No map names are defined in the default configuration. If all system
# user names and PostgreSQL user names are the same, you don't need
# this file.
# MAPNAME IDENT-USERNAME PG-USERNAME
# MAPNAME SYSTEM-USERNAME PG-USERNAME
......@@ -4,7 +4,7 @@
* Interface to hba.c
*
*
* $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.49 2008/09/15 12:32:57 mha Exp $
* $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.50 2008/10/23 13:31:10 mha Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -25,13 +25,9 @@ typedef enum UserAuth
uaCrypt,
uaMD5,
uaGSS,
uaSSPI
#ifdef USE_PAM
,uaPAM
#endif /* USE_PAM */
#ifdef USE_LDAP
,uaLDAP
#endif
uaSSPI,
uaPAM,
uaLDAP
} UserAuth;
typedef enum ConnType
......@@ -51,8 +47,14 @@ typedef struct
struct sockaddr_storage addr;
struct sockaddr_storage mask;
UserAuth auth_method;
char *usermap;
char *auth_arg;
char *pamservice;
bool ldaptls;
char *ldapserver;
int ldapport;
char *ldapprefix;
char *ldapsuffix;
} HbaLine;
typedef struct Port hbaPort;
......@@ -64,8 +66,9 @@ extern void load_role(void);
extern int hba_getauthmethod(hbaPort *port);
extern bool read_pg_database_line(FILE *fp, char *dbname, Oid *dboid,
Oid *dbtablespace, TransactionId *dbfrozenxid);
extern bool check_ident_usermap(const char *usermap_name,
const char *pg_role, const char *ident_user);
extern int check_usermap(const char *usermap_name,
const char *pg_role, const char *auth_user,
bool case_sensitive);
extern bool pg_isblank(const char c);
#endif /* HBA_H */
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册