mirror of
https://github.com/postgres/postgres.git
synced 2025-11-13 16:22:44 +03:00
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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user