Commit c30c1b87 authored by Magnus Hagander's avatar Magnus Hagander

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.
parent 63247bec
......@@ -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 */
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