diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index fc5e0f4a248..3470417f241 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -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 @@ -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 diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index 4054b898928..c552a4b2542 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -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 #include #include -#if defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || defined(HAVE_STRUCT_SOCKCRED) -#include -#include -#endif #include #include #include @@ -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 diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h index 603d8635238..16150846622 100644 --- a/src/include/libpq/hba.h +++ b/src/include/libpq/hba.h @@ -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 */