mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-12-27 08:39:28 +08:00
Move ident authentication code into auth.c along with the other authenciation
routines, leaving hba.c to deal only with processing the HBA specific files.
This commit is contained in:
parent
63247bec28
commit
c30c1b8786
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.165 2008/07/24 17:51:55 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.166 2008/08/01 09:09:49 mha Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -37,6 +37,7 @@ static void sendAuthRequest(Port *port, AuthRequest areq);
|
||||
static void auth_failed(Port *port, int status);
|
||||
static char *recv_password_packet(Port *port);
|
||||
static int recv_and_check_password_packet(Port *port);
|
||||
static int authident(hbaPort *port);
|
||||
|
||||
char *pg_krb_server_keyfile;
|
||||
char *pg_krb_srvnam;
|
||||
@ -44,6 +45,12 @@ bool pg_krb_caseins_users;
|
||||
char *pg_krb_server_hostname = NULL;
|
||||
char *pg_krb_realm = NULL;
|
||||
|
||||
/* Max size of username ident server can return */
|
||||
#define IDENT_USERNAME_MAX 512
|
||||
|
||||
/* Standard TCP port number for Ident service. Assigned by IANA */
|
||||
#define IDENT_PORT 113
|
||||
|
||||
#ifdef USE_PAM
|
||||
#ifdef HAVE_PAM_PAM_APPL_H
|
||||
#include <pam/pam_appl.h>
|
||||
@ -1194,6 +1201,460 @@ sendAuthRequest(Port *port, AuthRequest areq)
|
||||
pq_flush();
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------
|
||||
* Ident authentication system
|
||||
*----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* Parse the string "*ident_response" as a response from a query to an Ident
|
||||
* server. If it's a normal response indicating a user name, return true
|
||||
* and store the user name at *ident_user. If it's anything else,
|
||||
* return false.
|
||||
*/
|
||||
static bool
|
||||
interpret_ident_response(const char *ident_response,
|
||||
char *ident_user)
|
||||
{
|
||||
const char *cursor = ident_response; /* Cursor into *ident_response */
|
||||
|
||||
/*
|
||||
* Ident's response, in the telnet tradition, should end in crlf (\r\n).
|
||||
*/
|
||||
if (strlen(ident_response) < 2)
|
||||
return false;
|
||||
else if (ident_response[strlen(ident_response) - 2] != '\r')
|
||||
return false;
|
||||
else
|
||||
{
|
||||
while (*cursor != ':' && *cursor != '\r')
|
||||
cursor++; /* skip port field */
|
||||
|
||||
if (*cursor != ':')
|
||||
return false;
|
||||
else
|
||||
{
|
||||
/* We're positioned to colon before response type field */
|
||||
char response_type[80];
|
||||
int i; /* Index into *response_type */
|
||||
|
||||
cursor++; /* Go over colon */
|
||||
while (pg_isblank(*cursor))
|
||||
cursor++; /* skip blanks */
|
||||
i = 0;
|
||||
while (*cursor != ':' && *cursor != '\r' && !pg_isblank(*cursor) &&
|
||||
i < (int) (sizeof(response_type) - 1))
|
||||
response_type[i++] = *cursor++;
|
||||
response_type[i] = '\0';
|
||||
while (pg_isblank(*cursor))
|
||||
cursor++; /* skip blanks */
|
||||
if (strcmp(response_type, "USERID") != 0)
|
||||
return false;
|
||||
else
|
||||
{
|
||||
/*
|
||||
* It's a USERID response. Good. "cursor" should be pointing
|
||||
* to the colon that precedes the operating system type.
|
||||
*/
|
||||
if (*cursor != ':')
|
||||
return false;
|
||||
else
|
||||
{
|
||||
cursor++; /* Go over colon */
|
||||
/* Skip over operating system field. */
|
||||
while (*cursor != ':' && *cursor != '\r')
|
||||
cursor++;
|
||||
if (*cursor != ':')
|
||||
return false;
|
||||
else
|
||||
{
|
||||
int i; /* Index into *ident_user */
|
||||
|
||||
cursor++; /* Go over colon */
|
||||
while (pg_isblank(*cursor))
|
||||
cursor++; /* skip blanks */
|
||||
/* Rest of line is user name. Copy it over. */
|
||||
i = 0;
|
||||
while (*cursor != '\r' && i < IDENT_USERNAME_MAX)
|
||||
ident_user[i++] = *cursor++;
|
||||
ident_user[i] = '\0';
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Talk to the ident server on host "remote_ip_addr" and find out who
|
||||
* owns the tcp connection from his port "remote_port" to port
|
||||
* "local_port_addr" on host "local_ip_addr". Return the user name the
|
||||
* ident server gives as "*ident_user".
|
||||
*
|
||||
* IP addresses and port numbers are in network byte order.
|
||||
*
|
||||
* But iff we're unable to get the information from ident, return false.
|
||||
*/
|
||||
static bool
|
||||
ident_inet(const SockAddr remote_addr,
|
||||
const SockAddr local_addr,
|
||||
char *ident_user)
|
||||
{
|
||||
int sock_fd, /* File descriptor for socket on which we talk
|
||||
* to Ident */
|
||||
rc; /* Return code from a locally called function */
|
||||
bool ident_return;
|
||||
char remote_addr_s[NI_MAXHOST];
|
||||
char remote_port[NI_MAXSERV];
|
||||
char local_addr_s[NI_MAXHOST];
|
||||
char local_port[NI_MAXSERV];
|
||||
char ident_port[NI_MAXSERV];
|
||||
char ident_query[80];
|
||||
char ident_response[80 + IDENT_USERNAME_MAX];
|
||||
struct addrinfo *ident_serv = NULL,
|
||||
*la = NULL,
|
||||
hints;
|
||||
|
||||
/*
|
||||
* Might look a little weird to first convert it to text and then back to
|
||||
* sockaddr, but it's protocol independent.
|
||||
*/
|
||||
pg_getnameinfo_all(&remote_addr.addr, remote_addr.salen,
|
||||
remote_addr_s, sizeof(remote_addr_s),
|
||||
remote_port, sizeof(remote_port),
|
||||
NI_NUMERICHOST | NI_NUMERICSERV);
|
||||
pg_getnameinfo_all(&local_addr.addr, local_addr.salen,
|
||||
local_addr_s, sizeof(local_addr_s),
|
||||
local_port, sizeof(local_port),
|
||||
NI_NUMERICHOST | NI_NUMERICSERV);
|
||||
|
||||
snprintf(ident_port, sizeof(ident_port), "%d", IDENT_PORT);
|
||||
hints.ai_flags = AI_NUMERICHOST;
|
||||
hints.ai_family = remote_addr.addr.ss_family;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = 0;
|
||||
hints.ai_addrlen = 0;
|
||||
hints.ai_canonname = NULL;
|
||||
hints.ai_addr = NULL;
|
||||
hints.ai_next = NULL;
|
||||
rc = pg_getaddrinfo_all(remote_addr_s, ident_port, &hints, &ident_serv);
|
||||
if (rc || !ident_serv)
|
||||
{
|
||||
if (ident_serv)
|
||||
pg_freeaddrinfo_all(hints.ai_family, ident_serv);
|
||||
return false; /* we don't expect this to happen */
|
||||
}
|
||||
|
||||
hints.ai_flags = AI_NUMERICHOST;
|
||||
hints.ai_family = local_addr.addr.ss_family;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = 0;
|
||||
hints.ai_addrlen = 0;
|
||||
hints.ai_canonname = NULL;
|
||||
hints.ai_addr = NULL;
|
||||
hints.ai_next = NULL;
|
||||
rc = pg_getaddrinfo_all(local_addr_s, NULL, &hints, &la);
|
||||
if (rc || !la)
|
||||
{
|
||||
if (la)
|
||||
pg_freeaddrinfo_all(hints.ai_family, la);
|
||||
return false; /* we don't expect this to happen */
|
||||
}
|
||||
|
||||
sock_fd = socket(ident_serv->ai_family, ident_serv->ai_socktype,
|
||||
ident_serv->ai_protocol);
|
||||
if (sock_fd < 0)
|
||||
{
|
||||
ereport(LOG,
|
||||
(errcode_for_socket_access(),
|
||||
errmsg("could not create socket for Ident connection: %m")));
|
||||
ident_return = false;
|
||||
goto ident_inet_done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bind to the address which the client originally contacted, otherwise
|
||||
* the ident server won't be able to match up the right connection. This
|
||||
* is necessary if the PostgreSQL server is running on an IP alias.
|
||||
*/
|
||||
rc = bind(sock_fd, la->ai_addr, la->ai_addrlen);
|
||||
if (rc != 0)
|
||||
{
|
||||
ereport(LOG,
|
||||
(errcode_for_socket_access(),
|
||||
errmsg("could not bind to local address \"%s\": %m",
|
||||
local_addr_s)));
|
||||
ident_return = false;
|
||||
goto ident_inet_done;
|
||||
}
|
||||
|
||||
rc = connect(sock_fd, ident_serv->ai_addr,
|
||||
ident_serv->ai_addrlen);
|
||||
if (rc != 0)
|
||||
{
|
||||
ereport(LOG,
|
||||
(errcode_for_socket_access(),
|
||||
errmsg("could not connect to Ident server at address \"%s\", port %s: %m",
|
||||
remote_addr_s, ident_port)));
|
||||
ident_return = false;
|
||||
goto ident_inet_done;
|
||||
}
|
||||
|
||||
/* The query we send to the Ident server */
|
||||
snprintf(ident_query, sizeof(ident_query), "%s,%s\r\n",
|
||||
remote_port, local_port);
|
||||
|
||||
/* loop in case send is interrupted */
|
||||
do
|
||||
{
|
||||
rc = send(sock_fd, ident_query, strlen(ident_query), 0);
|
||||
} while (rc < 0 && errno == EINTR);
|
||||
|
||||
if (rc < 0)
|
||||
{
|
||||
ereport(LOG,
|
||||
(errcode_for_socket_access(),
|
||||
errmsg("could not send query to Ident server at address \"%s\", port %s: %m",
|
||||
remote_addr_s, ident_port)));
|
||||
ident_return = false;
|
||||
goto ident_inet_done;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
rc = recv(sock_fd, ident_response, sizeof(ident_response) - 1, 0);
|
||||
} while (rc < 0 && errno == EINTR);
|
||||
|
||||
if (rc < 0)
|
||||
{
|
||||
ereport(LOG,
|
||||
(errcode_for_socket_access(),
|
||||
errmsg("could not receive response from Ident server at address \"%s\", port %s: %m",
|
||||
remote_addr_s, ident_port)));
|
||||
ident_return = false;
|
||||
goto ident_inet_done;
|
||||
}
|
||||
|
||||
ident_response[rc] = '\0';
|
||||
ident_return = interpret_ident_response(ident_response, ident_user);
|
||||
if (!ident_return)
|
||||
ereport(LOG,
|
||||
(errmsg("invalidly formatted response from Ident server: \"%s\"",
|
||||
ident_response)));
|
||||
|
||||
ident_inet_done:
|
||||
if (sock_fd >= 0)
|
||||
closesocket(sock_fd);
|
||||
pg_freeaddrinfo_all(remote_addr.addr.ss_family, ident_serv);
|
||||
pg_freeaddrinfo_all(local_addr.addr.ss_family, la);
|
||||
return ident_return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifdef HAVE_UNIX_SOCKETS
|
||||
|
||||
static bool
|
||||
ident_unix(int sock, char *ident_user)
|
||||
{
|
||||
#if defined(HAVE_GETPEEREID)
|
||||
/* OpenBSD style: */
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
struct passwd *pass;
|
||||
|
||||
errno = 0;
|
||||
if (getpeereid(sock, &uid, &gid) != 0)
|
||||
{
|
||||
/* We didn't get a valid credentials struct. */
|
||||
ereport(LOG,
|
||||
(errcode_for_socket_access(),
|
||||
errmsg("could not get peer credentials: %m")));
|
||||
return false;
|
||||
}
|
||||
|
||||
pass = getpwuid(uid);
|
||||
|
||||
if (pass == NULL)
|
||||
{
|
||||
ereport(LOG,
|
||||
(errmsg("local user with ID %d does not exist",
|
||||
(int) uid)));
|
||||
return false;
|
||||
}
|
||||
|
||||
strlcpy(ident_user, pass->pw_name, IDENT_USERNAME_MAX + 1);
|
||||
|
||||
return true;
|
||||
#elif defined(SO_PEERCRED)
|
||||
/* Linux style: use getsockopt(SO_PEERCRED) */
|
||||
struct ucred peercred;
|
||||
ACCEPT_TYPE_ARG3 so_len = sizeof(peercred);
|
||||
struct passwd *pass;
|
||||
|
||||
errno = 0;
|
||||
if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0 ||
|
||||
so_len != sizeof(peercred))
|
||||
{
|
||||
/* We didn't get a valid credentials struct. */
|
||||
ereport(LOG,
|
||||
(errcode_for_socket_access(),
|
||||
errmsg("could not get peer credentials: %m")));
|
||||
return false;
|
||||
}
|
||||
|
||||
pass = getpwuid(peercred.uid);
|
||||
|
||||
if (pass == NULL)
|
||||
{
|
||||
ereport(LOG,
|
||||
(errmsg("local user with ID %d does not exist",
|
||||
(int) peercred.uid)));
|
||||
return false;
|
||||
}
|
||||
|
||||
strlcpy(ident_user, pass->pw_name, IDENT_USERNAME_MAX + 1);
|
||||
|
||||
return true;
|
||||
#elif defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS))
|
||||
struct msghdr msg;
|
||||
|
||||
/* Credentials structure */
|
||||
#if defined(HAVE_STRUCT_CMSGCRED)
|
||||
typedef struct cmsgcred Cred;
|
||||
|
||||
#define cruid cmcred_uid
|
||||
#elif defined(HAVE_STRUCT_FCRED)
|
||||
typedef struct fcred Cred;
|
||||
|
||||
#define cruid fc_uid
|
||||
#elif defined(HAVE_STRUCT_SOCKCRED)
|
||||
typedef struct sockcred Cred;
|
||||
|
||||
#define cruid sc_uid
|
||||
#endif
|
||||
Cred *cred;
|
||||
|
||||
/* Compute size without padding */
|
||||
char cmsgmem[ALIGN(sizeof(struct cmsghdr)) + ALIGN(sizeof(Cred))]; /* for NetBSD */
|
||||
|
||||
/* Point to start of first structure */
|
||||
struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem;
|
||||
|
||||
struct iovec iov;
|
||||
char buf;
|
||||
struct passwd *pw;
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = (char *) cmsg;
|
||||
msg.msg_controllen = sizeof(cmsgmem);
|
||||
memset(cmsg, 0, sizeof(cmsgmem));
|
||||
|
||||
/*
|
||||
* The one character which is received here is not meaningful; its
|
||||
* purposes is only to make sure that recvmsg() blocks long enough for the
|
||||
* other side to send its credentials.
|
||||
*/
|
||||
iov.iov_base = &buf;
|
||||
iov.iov_len = 1;
|
||||
|
||||
if (recvmsg(sock, &msg, 0) < 0 ||
|
||||
cmsg->cmsg_len < sizeof(cmsgmem) ||
|
||||
cmsg->cmsg_type != SCM_CREDS)
|
||||
{
|
||||
ereport(LOG,
|
||||
(errcode_for_socket_access(),
|
||||
errmsg("could not get peer credentials: %m")));
|
||||
return false;
|
||||
}
|
||||
|
||||
cred = (Cred *) CMSG_DATA(cmsg);
|
||||
|
||||
pw = getpwuid(cred->cruid);
|
||||
|
||||
if (pw == NULL)
|
||||
{
|
||||
ereport(LOG,
|
||||
(errmsg("local user with ID %d does not exist",
|
||||
(int) cred->cruid)));
|
||||
return false;
|
||||
}
|
||||
|
||||
strlcpy(ident_user, pw->pw_name, IDENT_USERNAME_MAX + 1);
|
||||
|
||||
return true;
|
||||
#else
|
||||
ereport(LOG,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("Ident authentication is not supported on local connections on this platform")));
|
||||
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
#endif /* HAVE_UNIX_SOCKETS */
|
||||
|
||||
|
||||
/*
|
||||
* 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, STATUS_ERROR if no match (or couldn't get info).
|
||||
*/
|
||||
static int
|
||||
authident(hbaPort *port)
|
||||
{
|
||||
char ident_user[IDENT_USERNAME_MAX + 1];
|
||||
|
||||
if (get_role_line(port->user_name) == NULL)
|
||||
return STATUS_ERROR;
|
||||
|
||||
switch (port->raddr.addr.ss_family)
|
||||
{
|
||||
case AF_INET:
|
||||
#ifdef HAVE_IPV6
|
||||
case AF_INET6:
|
||||
#endif
|
||||
if (!ident_inet(port->raddr, port->laddr, ident_user))
|
||||
return STATUS_ERROR;
|
||||
break;
|
||||
|
||||
#ifdef HAVE_UNIX_SOCKETS
|
||||
case AF_UNIX:
|
||||
if (!ident_unix(port->sock, ident_user))
|
||||
return STATUS_ERROR;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return STATUS_ERROR;
|
||||
}
|
||||
|
||||
ereport(DEBUG2,
|
||||
(errmsg("Ident protocol identifies remote user as \"%s\"",
|
||||
ident_user)));
|
||||
|
||||
if (check_ident_usermap(port->auth_arg, port->user_name, ident_user))
|
||||
return STATUS_OK;
|
||||
else
|
||||
return STATUS_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------
|
||||
* PAM authentication system
|
||||
*----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifdef USE_PAM
|
||||
|
||||
|
@ -3,14 +3,14 @@
|
||||
* hba.c
|
||||
* Routines to handle host based authentication (that's the scheme
|
||||
* wherein you authenticate a user by seeing what IP address the system
|
||||
* says he comes from and possibly using ident).
|
||||
* says he comes from and choosing authentication method based on it).
|
||||
*
|
||||
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.165 2008/07/24 17:43:45 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.166 2008/08/01 09:09:49 mha Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -21,10 +21,6 @@
|
||||
#include <fcntl.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/socket.h>
|
||||
#if defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || defined(HAVE_STRUCT_SOCKCRED)
|
||||
#include <sys/uio.h>
|
||||
#include <sys/ucred.h>
|
||||
#endif
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
@ -40,12 +36,6 @@
|
||||
#define atooid(x) ((Oid) strtoul((x), NULL, 10))
|
||||
#define atoxid(x) ((TransactionId) strtoul((x), NULL, 10))
|
||||
|
||||
/* Max size of username ident server can return */
|
||||
#define IDENT_USERNAME_MAX 512
|
||||
|
||||
/* Standard TCP port number for Ident service. Assigned by IANA */
|
||||
#define IDENT_PORT 113
|
||||
|
||||
/* This is used to separate values in multi-valued column strings */
|
||||
#define MULTI_VALUE_SEP "\001"
|
||||
|
||||
@ -87,7 +77,7 @@ static char *tokenize_inc_file(const char *outer_filename,
|
||||
* isblank() exists in the ISO C99 spec, but it's not very portable yet,
|
||||
* so provide our own version.
|
||||
*/
|
||||
static bool
|
||||
bool
|
||||
pg_isblank(const char c)
|
||||
{
|
||||
return c == ' ' || c == '\t' || c == '\r';
|
||||
@ -1116,7 +1106,7 @@ ident_syntax:
|
||||
*
|
||||
* Iff authorized, return true.
|
||||
*/
|
||||
static bool
|
||||
bool
|
||||
check_ident_usermap(const char *usermap_name,
|
||||
const char *pg_role,
|
||||
const char *ident_user)
|
||||
@ -1185,450 +1175,6 @@ load_ident(void)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Parse the string "*ident_response" as a response from a query to an Ident
|
||||
* server. If it's a normal response indicating a user name, return true
|
||||
* and store the user name at *ident_user. If it's anything else,
|
||||
* return false.
|
||||
*/
|
||||
static bool
|
||||
interpret_ident_response(const char *ident_response,
|
||||
char *ident_user)
|
||||
{
|
||||
const char *cursor = ident_response; /* Cursor into *ident_response */
|
||||
|
||||
/*
|
||||
* Ident's response, in the telnet tradition, should end in crlf (\r\n).
|
||||
*/
|
||||
if (strlen(ident_response) < 2)
|
||||
return false;
|
||||
else if (ident_response[strlen(ident_response) - 2] != '\r')
|
||||
return false;
|
||||
else
|
||||
{
|
||||
while (*cursor != ':' && *cursor != '\r')
|
||||
cursor++; /* skip port field */
|
||||
|
||||
if (*cursor != ':')
|
||||
return false;
|
||||
else
|
||||
{
|
||||
/* We're positioned to colon before response type field */
|
||||
char response_type[80];
|
||||
int i; /* Index into *response_type */
|
||||
|
||||
cursor++; /* Go over colon */
|
||||
while (pg_isblank(*cursor))
|
||||
cursor++; /* skip blanks */
|
||||
i = 0;
|
||||
while (*cursor != ':' && *cursor != '\r' && !pg_isblank(*cursor) &&
|
||||
i < (int) (sizeof(response_type) - 1))
|
||||
response_type[i++] = *cursor++;
|
||||
response_type[i] = '\0';
|
||||
while (pg_isblank(*cursor))
|
||||
cursor++; /* skip blanks */
|
||||
if (strcmp(response_type, "USERID") != 0)
|
||||
return false;
|
||||
else
|
||||
{
|
||||
/*
|
||||
* It's a USERID response. Good. "cursor" should be pointing
|
||||
* to the colon that precedes the operating system type.
|
||||
*/
|
||||
if (*cursor != ':')
|
||||
return false;
|
||||
else
|
||||
{
|
||||
cursor++; /* Go over colon */
|
||||
/* Skip over operating system field. */
|
||||
while (*cursor != ':' && *cursor != '\r')
|
||||
cursor++;
|
||||
if (*cursor != ':')
|
||||
return false;
|
||||
else
|
||||
{
|
||||
int i; /* Index into *ident_user */
|
||||
|
||||
cursor++; /* Go over colon */
|
||||
while (pg_isblank(*cursor))
|
||||
cursor++; /* skip blanks */
|
||||
/* Rest of line is user name. Copy it over. */
|
||||
i = 0;
|
||||
while (*cursor != '\r' && i < IDENT_USERNAME_MAX)
|
||||
ident_user[i++] = *cursor++;
|
||||
ident_user[i] = '\0';
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Talk to the ident server on host "remote_ip_addr" and find out who
|
||||
* owns the tcp connection from his port "remote_port" to port
|
||||
* "local_port_addr" on host "local_ip_addr". Return the user name the
|
||||
* ident server gives as "*ident_user".
|
||||
*
|
||||
* IP addresses and port numbers are in network byte order.
|
||||
*
|
||||
* But iff we're unable to get the information from ident, return false.
|
||||
*/
|
||||
static bool
|
||||
ident_inet(const SockAddr remote_addr,
|
||||
const SockAddr local_addr,
|
||||
char *ident_user)
|
||||
{
|
||||
int sock_fd, /* File descriptor for socket on which we talk
|
||||
* to Ident */
|
||||
rc; /* Return code from a locally called function */
|
||||
bool ident_return;
|
||||
char remote_addr_s[NI_MAXHOST];
|
||||
char remote_port[NI_MAXSERV];
|
||||
char local_addr_s[NI_MAXHOST];
|
||||
char local_port[NI_MAXSERV];
|
||||
char ident_port[NI_MAXSERV];
|
||||
char ident_query[80];
|
||||
char ident_response[80 + IDENT_USERNAME_MAX];
|
||||
struct addrinfo *ident_serv = NULL,
|
||||
*la = NULL,
|
||||
hints;
|
||||
|
||||
/*
|
||||
* Might look a little weird to first convert it to text and then back to
|
||||
* sockaddr, but it's protocol independent.
|
||||
*/
|
||||
pg_getnameinfo_all(&remote_addr.addr, remote_addr.salen,
|
||||
remote_addr_s, sizeof(remote_addr_s),
|
||||
remote_port, sizeof(remote_port),
|
||||
NI_NUMERICHOST | NI_NUMERICSERV);
|
||||
pg_getnameinfo_all(&local_addr.addr, local_addr.salen,
|
||||
local_addr_s, sizeof(local_addr_s),
|
||||
local_port, sizeof(local_port),
|
||||
NI_NUMERICHOST | NI_NUMERICSERV);
|
||||
|
||||
snprintf(ident_port, sizeof(ident_port), "%d", IDENT_PORT);
|
||||
hints.ai_flags = AI_NUMERICHOST;
|
||||
hints.ai_family = remote_addr.addr.ss_family;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = 0;
|
||||
hints.ai_addrlen = 0;
|
||||
hints.ai_canonname = NULL;
|
||||
hints.ai_addr = NULL;
|
||||
hints.ai_next = NULL;
|
||||
rc = pg_getaddrinfo_all(remote_addr_s, ident_port, &hints, &ident_serv);
|
||||
if (rc || !ident_serv)
|
||||
{
|
||||
if (ident_serv)
|
||||
pg_freeaddrinfo_all(hints.ai_family, ident_serv);
|
||||
return false; /* we don't expect this to happen */
|
||||
}
|
||||
|
||||
hints.ai_flags = AI_NUMERICHOST;
|
||||
hints.ai_family = local_addr.addr.ss_family;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = 0;
|
||||
hints.ai_addrlen = 0;
|
||||
hints.ai_canonname = NULL;
|
||||
hints.ai_addr = NULL;
|
||||
hints.ai_next = NULL;
|
||||
rc = pg_getaddrinfo_all(local_addr_s, NULL, &hints, &la);
|
||||
if (rc || !la)
|
||||
{
|
||||
if (la)
|
||||
pg_freeaddrinfo_all(hints.ai_family, la);
|
||||
return false; /* we don't expect this to happen */
|
||||
}
|
||||
|
||||
sock_fd = socket(ident_serv->ai_family, ident_serv->ai_socktype,
|
||||
ident_serv->ai_protocol);
|
||||
if (sock_fd < 0)
|
||||
{
|
||||
ereport(LOG,
|
||||
(errcode_for_socket_access(),
|
||||
errmsg("could not create socket for Ident connection: %m")));
|
||||
ident_return = false;
|
||||
goto ident_inet_done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bind to the address which the client originally contacted, otherwise
|
||||
* the ident server won't be able to match up the right connection. This
|
||||
* is necessary if the PostgreSQL server is running on an IP alias.
|
||||
*/
|
||||
rc = bind(sock_fd, la->ai_addr, la->ai_addrlen);
|
||||
if (rc != 0)
|
||||
{
|
||||
ereport(LOG,
|
||||
(errcode_for_socket_access(),
|
||||
errmsg("could not bind to local address \"%s\": %m",
|
||||
local_addr_s)));
|
||||
ident_return = false;
|
||||
goto ident_inet_done;
|
||||
}
|
||||
|
||||
rc = connect(sock_fd, ident_serv->ai_addr,
|
||||
ident_serv->ai_addrlen);
|
||||
if (rc != 0)
|
||||
{
|
||||
ereport(LOG,
|
||||
(errcode_for_socket_access(),
|
||||
errmsg("could not connect to Ident server at address \"%s\", port %s: %m",
|
||||
remote_addr_s, ident_port)));
|
||||
ident_return = false;
|
||||
goto ident_inet_done;
|
||||
}
|
||||
|
||||
/* The query we send to the Ident server */
|
||||
snprintf(ident_query, sizeof(ident_query), "%s,%s\r\n",
|
||||
remote_port, local_port);
|
||||
|
||||
/* loop in case send is interrupted */
|
||||
do
|
||||
{
|
||||
rc = send(sock_fd, ident_query, strlen(ident_query), 0);
|
||||
} while (rc < 0 && errno == EINTR);
|
||||
|
||||
if (rc < 0)
|
||||
{
|
||||
ereport(LOG,
|
||||
(errcode_for_socket_access(),
|
||||
errmsg("could not send query to Ident server at address \"%s\", port %s: %m",
|
||||
remote_addr_s, ident_port)));
|
||||
ident_return = false;
|
||||
goto ident_inet_done;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
rc = recv(sock_fd, ident_response, sizeof(ident_response) - 1, 0);
|
||||
} while (rc < 0 && errno == EINTR);
|
||||
|
||||
if (rc < 0)
|
||||
{
|
||||
ereport(LOG,
|
||||
(errcode_for_socket_access(),
|
||||
errmsg("could not receive response from Ident server at address \"%s\", port %s: %m",
|
||||
remote_addr_s, ident_port)));
|
||||
ident_return = false;
|
||||
goto ident_inet_done;
|
||||
}
|
||||
|
||||
ident_response[rc] = '\0';
|
||||
ident_return = interpret_ident_response(ident_response, ident_user);
|
||||
if (!ident_return)
|
||||
ereport(LOG,
|
||||
(errmsg("invalidly formatted response from Ident server: \"%s\"",
|
||||
ident_response)));
|
||||
|
||||
ident_inet_done:
|
||||
if (sock_fd >= 0)
|
||||
closesocket(sock_fd);
|
||||
pg_freeaddrinfo_all(remote_addr.addr.ss_family, ident_serv);
|
||||
pg_freeaddrinfo_all(local_addr.addr.ss_family, la);
|
||||
return ident_return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifdef HAVE_UNIX_SOCKETS
|
||||
|
||||
static bool
|
||||
ident_unix(int sock, char *ident_user)
|
||||
{
|
||||
#if defined(HAVE_GETPEEREID)
|
||||
/* OpenBSD style: */
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
struct passwd *pass;
|
||||
|
||||
errno = 0;
|
||||
if (getpeereid(sock, &uid, &gid) != 0)
|
||||
{
|
||||
/* We didn't get a valid credentials struct. */
|
||||
ereport(LOG,
|
||||
(errcode_for_socket_access(),
|
||||
errmsg("could not get peer credentials: %m")));
|
||||
return false;
|
||||
}
|
||||
|
||||
pass = getpwuid(uid);
|
||||
|
||||
if (pass == NULL)
|
||||
{
|
||||
ereport(LOG,
|
||||
(errmsg("local user with ID %d does not exist",
|
||||
(int) uid)));
|
||||
return false;
|
||||
}
|
||||
|
||||
strlcpy(ident_user, pass->pw_name, IDENT_USERNAME_MAX + 1);
|
||||
|
||||
return true;
|
||||
#elif defined(SO_PEERCRED)
|
||||
/* Linux style: use getsockopt(SO_PEERCRED) */
|
||||
struct ucred peercred;
|
||||
ACCEPT_TYPE_ARG3 so_len = sizeof(peercred);
|
||||
struct passwd *pass;
|
||||
|
||||
errno = 0;
|
||||
if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0 ||
|
||||
so_len != sizeof(peercred))
|
||||
{
|
||||
/* We didn't get a valid credentials struct. */
|
||||
ereport(LOG,
|
||||
(errcode_for_socket_access(),
|
||||
errmsg("could not get peer credentials: %m")));
|
||||
return false;
|
||||
}
|
||||
|
||||
pass = getpwuid(peercred.uid);
|
||||
|
||||
if (pass == NULL)
|
||||
{
|
||||
ereport(LOG,
|
||||
(errmsg("local user with ID %d does not exist",
|
||||
(int) peercred.uid)));
|
||||
return false;
|
||||
}
|
||||
|
||||
strlcpy(ident_user, pass->pw_name, IDENT_USERNAME_MAX + 1);
|
||||
|
||||
return true;
|
||||
#elif defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS))
|
||||
struct msghdr msg;
|
||||
|
||||
/* Credentials structure */
|
||||
#if defined(HAVE_STRUCT_CMSGCRED)
|
||||
typedef struct cmsgcred Cred;
|
||||
|
||||
#define cruid cmcred_uid
|
||||
#elif defined(HAVE_STRUCT_FCRED)
|
||||
typedef struct fcred Cred;
|
||||
|
||||
#define cruid fc_uid
|
||||
#elif defined(HAVE_STRUCT_SOCKCRED)
|
||||
typedef struct sockcred Cred;
|
||||
|
||||
#define cruid sc_uid
|
||||
#endif
|
||||
Cred *cred;
|
||||
|
||||
/* Compute size without padding */
|
||||
char cmsgmem[ALIGN(sizeof(struct cmsghdr)) + ALIGN(sizeof(Cred))]; /* for NetBSD */
|
||||
|
||||
/* Point to start of first structure */
|
||||
struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem;
|
||||
|
||||
struct iovec iov;
|
||||
char buf;
|
||||
struct passwd *pw;
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = (char *) cmsg;
|
||||
msg.msg_controllen = sizeof(cmsgmem);
|
||||
memset(cmsg, 0, sizeof(cmsgmem));
|
||||
|
||||
/*
|
||||
* The one character which is received here is not meaningful; its
|
||||
* purposes is only to make sure that recvmsg() blocks long enough for the
|
||||
* other side to send its credentials.
|
||||
*/
|
||||
iov.iov_base = &buf;
|
||||
iov.iov_len = 1;
|
||||
|
||||
if (recvmsg(sock, &msg, 0) < 0 ||
|
||||
cmsg->cmsg_len < sizeof(cmsgmem) ||
|
||||
cmsg->cmsg_type != SCM_CREDS)
|
||||
{
|
||||
ereport(LOG,
|
||||
(errcode_for_socket_access(),
|
||||
errmsg("could not get peer credentials: %m")));
|
||||
return false;
|
||||
}
|
||||
|
||||
cred = (Cred *) CMSG_DATA(cmsg);
|
||||
|
||||
pw = getpwuid(cred->cruid);
|
||||
|
||||
if (pw == NULL)
|
||||
{
|
||||
ereport(LOG,
|
||||
(errmsg("local user with ID %d does not exist",
|
||||
(int) cred->cruid)));
|
||||
return false;
|
||||
}
|
||||
|
||||
strlcpy(ident_user, pw->pw_name, IDENT_USERNAME_MAX + 1);
|
||||
|
||||
return true;
|
||||
#else
|
||||
ereport(LOG,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("Ident authentication is not supported on local connections on this platform")));
|
||||
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
#endif /* HAVE_UNIX_SOCKETS */
|
||||
|
||||
|
||||
/*
|
||||
* 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, STATUS_ERROR if no match (or couldn't get info).
|
||||
*/
|
||||
int
|
||||
authident(hbaPort *port)
|
||||
{
|
||||
char ident_user[IDENT_USERNAME_MAX + 1];
|
||||
|
||||
if (get_role_line(port->user_name) == NULL)
|
||||
return STATUS_ERROR;
|
||||
|
||||
switch (port->raddr.addr.ss_family)
|
||||
{
|
||||
case AF_INET:
|
||||
#ifdef HAVE_IPV6
|
||||
case AF_INET6:
|
||||
#endif
|
||||
if (!ident_inet(port->raddr, port->laddr, ident_user))
|
||||
return STATUS_ERROR;
|
||||
break;
|
||||
|
||||
#ifdef HAVE_UNIX_SOCKETS
|
||||
case AF_UNIX:
|
||||
if (!ident_unix(port->sock, ident_user))
|
||||
return STATUS_ERROR;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return STATUS_ERROR;
|
||||
}
|
||||
|
||||
ereport(DEBUG2,
|
||||
(errmsg("Ident protocol identifies remote user as \"%s\"",
|
||||
ident_user)));
|
||||
|
||||
if (check_ident_usermap(port->auth_arg, port->user_name, ident_user))
|
||||
return STATUS_OK;
|
||||
else
|
||||
return STATUS_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Determine what authentication method should be used when accessing database
|
||||
|
@ -4,7 +4,7 @@
|
||||
* Interface to hba.c
|
||||
*
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.47 2007/07/23 10:16:54 mha Exp $
|
||||
* $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.48 2008/08/01 09:09:48 mha Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -40,8 +40,10 @@ extern void load_hba(void);
|
||||
extern void load_ident(void);
|
||||
extern void load_role(void);
|
||||
extern int hba_getauthmethod(hbaPort *port);
|
||||
extern int authident(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 bool pg_isblank(const char c);
|
||||
|
||||
#endif /* HBA_H */
|
||||
|
Loading…
Reference in New Issue
Block a user