routines, leaving hba.c to deal only with processing the HBA specific files.
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
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>
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
* 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 $
*
*-------------------------------------------------------------------------
*/
#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>
#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"
* 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';
*
* Iff authorized, return true.
*/
-static bool
+bool
check_ident_usermap(const char *usermap_name,
const char *pg_role,
const char *ident_user)
}
-/*
- * 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
* 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 $
*
*-------------------------------------------------------------------------
*/
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 */