Commit bc042e0a authored by Tom Lane's avatar Tom Lane

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.
parent 72085187
This diff is collapsed.
......@@ -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>
......
<!-- $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>
......@@ -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:
......
......@@ -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;
......
......@@ -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.
#
# krb4: Kerberos V4 authentication is used.
#
# krb5: Kerberos V5 authentication is used.
# 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. Allowed only for
# TCP/IP connections, not for local UNIX-domain sockets.
#
# 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
......
......@@ -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.
......
......@@ -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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment