Support ident authentication on local (Unix) socket connections, if the

system supports SO_PEERCRED requests for Unix sockets.  This is an
amalgamation of patches submitted by Helge Bahmann and Oliver Elphick,
with some editorializing by yours truly.
This commit is contained in:
Tom Lane 2001-08-01 23:25:39 +00:00
parent 7208518720
commit bc042e0a77
8 changed files with 358 additions and 196 deletions

298
configure vendored

File diff suppressed because it is too large Load Diff

View File

@ -801,6 +801,19 @@ AC_CHECK_FUNCS([fcvt getopt_long memmove pstat setproctitle setsid sigprocmask s
dnl Check whether <unistd.h> declares fdatasync().
AC_EGREP_HEADER(fdatasync, unistd.h, AC_DEFINE(HAVE_FDATASYNC_DECL))
AC_MSG_CHECKING([for SO_PEERCRED])
AC_EGREP_CPP(HAVE_SO_PEERCRED,
#include <sys/socket.h>
#ifdef SO_PEERCRED
HAVE_SO_PEERCRED
#endif
],
[
AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_SO_PEERCRED)
],
[AC_MSG_RESULT(no)])
AC_CACHE_CHECK([for PS_STRINGS], [pgac_cv_var_PS_STRINGS],
[AC_TRY_LINK(
[#include <machine/vmparam.h>

View File

@ -1,4 +1,4 @@
<!-- $Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.14 2001/08/01 00:48:52 momjian Exp $ -->
<!-- $Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.15 2001/08/01 23:25:39 tgl Exp $ -->
<chapter id="client-authentication">
<title>Client Authentication</title>
@ -237,14 +237,28 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable
<varlistentry>
<term>ident</term>
<listitem>
<para>
The identity of the user as determined on login to the
operating system is used by <productname>Postgres</productname>
to determine whether the user
is allowed to connect as the requested database user.
For TCP/IP connections the user's identity is determined by
contacting the <firstterm>ident</firstterm> server on the client
host. (Note that this is only as reliable as the remote ident
server; ident authentication should never be used for remote hosts
whose administrators are not trustworthy.)
On operating systems
supporting SO_PEERCRED requests for Unix domain sockets,
ident authentication is possible for local connections;
the system is then asked for the connecting user's identity.
</para>
<para>
The ident server on the client host is asked for the identity
of the connecting user. <productname>Postgres</productname>
then verifies whether the so identified operating system user
is allowed to connect as the database user that is requested.
This is only available for TCP/IP connections. It can be used
on the local machine by specifying the localhost address 127.0.0.1.
</para>
On systems without SO_PEERCRED requests, ident authentication
is only available for TCP/IP connections. As a workaround,
it is possible to
specify the localhost address 127.0.0.1 and make connections
to this address.
</para>
<para>
The <replaceable>authentication option</replaceable> following
the <literal>ident</> keyword specifies the name of an
@ -283,7 +297,8 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable
The <filename>pg_hba.conf</filename> file is loaded only on startup
and when the <application>postmaster</> receives a SIGHUP signal. If
you edit the file on an active system, you will need to issue a
SIGHUP to the <application>postmaster</> using <application>kill</>.
SIGHUP to the <application>postmaster</> using <application>kill</>
to make it re-read the file.
</para>
<para>
@ -563,11 +578,19 @@ host all 192.168.0.0 255.255.0.0 ident omicron
You must trust the machine running the ident server.
</para>
<para>
On systems supporting SO_PEERCRED requests for Unix-domain sockets,
ident authentication can also be applied to local connections. In this
case, no security risk is added by using ident authentication; indeed
it is a preferable choice for such a system.
</para>
<para>
When using ident-based authentication, after having determined the
operating system user that initiated the connection,
<productname>Postgres</productname> determines as what database
system user he may connect. This is controlled by the ident map
name of the operating system user that initiated the connection,
<productname>Postgres</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</> keyword in the
<filename>pg_hba.conf</filename> file. The simplest ident map is
<literal>sameuser</literal>, which allows any operating system
@ -588,8 +611,9 @@ host all 192.168.0.0 255.255.0.0 ident omicron
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. There is also no restriction regarding how many
database users a given operating system may correspond to and vice
user-mappings within a single map. There is no restriction regarding
how many
database users a given operating system user may correspond to and vice
versa.
</para>
@ -669,6 +693,12 @@ FATAL 1: Database "testdb" does not exist in the system catalog.
if you don't specify a database name, it defaults to the database
user name, which may or may not be the right thing.
</para>
<para>
Note that the postmaster's stderr log may contain more information
about an authentication failure than is reported to the client.
If you are confused about the reason for a failure, check the log.
</para>
</sect1>
</chapter>

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.54 2001/07/21 00:29:56 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.55 2001/08/01 23:25:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -494,8 +494,7 @@ ClientAuthentication(Port *port)
break;
case uaIdent:
status = authident(&port->raddr.in, &port->laddr.in,
port->user, port->auth_arg);
status = authident(port);
break;
case uaPassword:
@ -654,8 +653,7 @@ map_old_to_new(Port *port, UserAuth old, int status)
break;
case uaIdent:
status = authident(&port->raddr.in, &port->laddr.in,
port->user, port->auth_arg);
status = authident(port);
break;
case uaPassword:

View File

@ -5,7 +5,7 @@
* wherein you authenticate a user by seeing what IP address the system
* says he comes from and possibly using ident).
*
* $Id: hba.c,v 1.57 2001/07/31 22:55:45 tgl Exp $
* $Id: hba.c,v 1.58 2001/08/01 23:25:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -286,12 +286,25 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p)
/*
* Disallow auth methods that need AF_INET sockets to work.
* Allow "ident" if we can get the identity of the connection
* peer on Unix domain sockets from the OS.
*/
if (!*error_p &&
(port->auth_method == uaIdent ||
port->auth_method == uaKrb4 ||
port->auth_method == uaKrb5))
if (port->auth_method == uaKrb4 ||
port->auth_method == uaKrb5)
goto hba_syntax;
#ifndef HAVE_SO_PEERCRED
if (port->auth_method == uaIdent)
{
/* Give a special error message for this case... */
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
"parse_hba: \"ident\" auth is not supported on local connections on this platform\n");
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
*error_p = true;
return;
}
#endif
/*
* If this record doesn't match the parameters of the connection
@ -732,12 +745,12 @@ interpret_ident_response(char *ident_response,
*
* But iff we're unable to get the information from ident, return false.
*/
static int
ident(const struct in_addr remote_ip_addr,
const struct in_addr local_ip_addr,
const ushort remote_port,
const ushort local_port,
char *ident_user)
static bool
ident_inet(const struct in_addr remote_ip_addr,
const struct in_addr local_ip_addr,
const ushort remote_port,
const ushort local_port,
char *ident_user)
{
int sock_fd, /* File descriptor for socket on which we
* talk to Ident */
@ -848,28 +861,103 @@ ident(const struct in_addr remote_ip_addr,
return ident_return;
}
#ifdef HAVE_SO_PEERCRED
/*
* Ask kernel about the credentials of the connecting process and
* determine the symbolic name of the corresponding user.
*
* Returns either true and the username put into "ident_user",
* or false if we were unable to determine the username.
*/
static bool
ident_unix(int sock, char *ident_user)
{
struct ucred peercred;
socklen_t so_len;
struct passwd *pass;
#ifdef SO_PASSCRED
int passcred = -1;
so_len = sizeof(passcred);
if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &passcred, so_len) != 0)
{
/* We could not set the socket to pass credentials */
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
"Could not set the UNIX socket to pass credentials: %s\n",
strerror(errno));
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
return false;
}
#endif /* SO_PASSCRED */
errno = 0;
so_len = sizeof(peercred);
if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0 ||
so_len != sizeof(peercred))
{
/* We didn't get a valid credentials struct. */
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
"Could not get valid credentials from the UNIX socket: %s\n",
strerror(errno));
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
return false;
}
/* Convert UID to user login name */
pass = getpwuid(peercred.uid);
if (pass == NULL)
{
/* Error - no username with the given uid */
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
"There is no entry in /etc/passwd with the socket's uid\n");
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
return false;
}
StrNCpy(ident_user, pass->pw_name, IDENT_USERNAME_MAX);
return true;
}
#endif
/*
* Talk to the ident server on the remote host and find out who owns the
* connection described by "port". Then look in the usermap file under
* the usermap *auth_arg and see if that user is equivalent to
* Postgres user *user.
* Determine the username of the initiator of the connection described
* by "port". Then look in the usermap file under the usermap
* port->auth_arg and see if that user is equivalent to Postgres user
* port->user.
*
* Return STATUS_OK if yes.
* Return STATUS_OK if yes, STATUS_ERROR if no match (or couldn't get info).
*/
int
authident(struct sockaddr_in *raddr, struct sockaddr_in *laddr,
const char *pg_user, const char *auth_arg)
authident(hbaPort *port)
{
/* We were unable to get ident to give us a username */
char ident_user[IDENT_USERNAME_MAX + 1];
/* The username returned by ident */
if (!ident(raddr->sin_addr, laddr->sin_addr,
raddr->sin_port, laddr->sin_port, ident_user))
return STATUS_ERROR;
switch (port->raddr.sa.sa_family)
{
case AF_INET:
if (!ident_inet(port->raddr.in.sin_addr,
port->laddr.in.sin_addr,
port->raddr.in.sin_port,
port->laddr.in.sin_port, ident_user))
return STATUS_ERROR;
break;
#ifdef HAVE_SO_PEERCRED
case AF_UNIX:
if (!ident_unix(port->sock, ident_user))
return STATUS_ERROR;
break;
#endif
default:
return STATUS_ERROR;
}
if (check_ident_usermap(auth_arg, pg_user, ident_user))
if (check_ident_usermap(port->auth_arg, port->user, ident_user))
return STATUS_OK;
else
return STATUS_ERROR;

View File

@ -126,28 +126,31 @@
# usernames stored in secondary password files but not
# secondary passwords.
#
# ident: Authentication is done by the ident server on the local
# (127.0.0.1) or remote host. AUTH_ARGUMENT is required and
# maps names found in the $PGDATA/pg_ident.conf file. The
# connection is accepted if the file contains an entry for
# this map name with the ident-supplied username and the
# requested PostgreSQL username. The special map name
# "sameuser" indicates an implied map (not in pg_ident.conf)
# that maps each ident username to the identical PostgreSQL
# username.
# ident: For TCP/IP connections, authentication is done by contacting
# the ident server on the client host. (CAUTION: this is only
# as secure as the client machine!) On machines that support
# SO_PEERCRED socket requests, this method also works for
# local Unix-domain connections. AUTH_ARGUMENT is required:
# it determines how to map remote user names to Postgres user
# names. The AUTH_ARGUMENT is a map name found in the
# $PGDATA/pg_ident.conf file. The connection is accepted if
# that file contains an entry for this map name with the
# ident-supplied username and the requested Postgres username.
# The special map name "sameuser" indicates an implied map
# (not in pg_ident.conf) that maps each ident username to the
# identical PostgreSQL username.
#
# krb4: Kerberos V4 authentication is used.
# krb4: Kerberos V4 authentication is used. Allowed only for
# TCP/IP connections, not for local UNIX-domain sockets.
#
# krb5: Kerberos V5 authentication is used.
# krb5: Kerberos V5 authentication is used. Allowed only for
# TCP/IP connections, not for local UNIX-domain sockets.
#
# reject: Reject the connection. This is used to reject certain hosts
# that are part of a network specified later in the file.
# To be effective, "reject" must appear before the later
# entries.
#
# Local UNIX-domain socket connections support only the AUTH_TYPEs of
# "trust", "password", "crypt", and "reject".
#
#
#
# Examples

View File

@ -8,7 +8,7 @@
* or in config.h afterwards. Of course, if you edit config.h, then your
* changes will be overwritten the next time you run configure.
*
* $Id: config.h.in,v 1.168 2001/07/16 05:07:00 tgl Exp $
* $Id: config.h.in,v 1.169 2001/08/01 23:25:39 tgl Exp $
*/
#ifndef CONFIG_H
@ -685,6 +685,9 @@ extern int fdatasync(int fildes);
/* Define if you have on_exit() */
#undef HAVE_ON_EXIT
/* Define if you have SO_PEERCRED */
#undef HAVE_SO_PEERCRED
/*
*------------------------------------------------------------------------
* Part 4: pull in system-specific declarations.

View File

@ -4,7 +4,7 @@
* Interface to hba.c
*
*
* $Id: hba.h,v 1.21 2001/07/31 22:55:45 tgl Exp $
* $Id: hba.h,v 1.22 2001/08/01 23:25:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -41,8 +41,7 @@ typedef enum UserAuth
typedef struct Port hbaPort;
extern int hba_getauthmethod(hbaPort *port);
extern int authident(struct sockaddr_in *raddr, struct sockaddr_in *laddr,
const char *postgres_username, const char *auth_arg);
extern int authident(hbaPort *port);
extern void load_hba_and_ident(void);
#endif