diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/libpq/auth.c | 183 | ||||
-rw-r--r-- | src/include/pg_config.h.in | 9 | ||||
-rw-r--r-- | src/include/pg_config.h.win32 | 9 | ||||
-rw-r--r-- | src/interfaces/libpq/fe-auth.c | 22 | ||||
-rw-r--r-- | src/interfaces/libpq/fe-connect.c | 32 |
5 files changed, 78 insertions, 177 deletions
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index e6ab659f4b1..dc7ad2cadf4 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -17,17 +17,15 @@ #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 #ifdef HAVE_UCRED_H #include <ucred.h> #endif +#ifdef HAVE_SYS_UCRED_H +#include <sys/ucred.h> +#endif #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> -#include <stddef.h> #include "libpq/auth.h" #include "libpq/crypt.h" @@ -515,36 +513,8 @@ ClientAuthentication(Port *port) case uaPeer: #ifdef HAVE_UNIX_SOCKETS - - /* - * If we are doing peer on unix-domain sockets, use SCM_CREDS only - * if it is defined and SO_PEERCRED isn't. - */ -#if !defined(HAVE_GETPEEREID) && !defined(SO_PEERCRED) && \ - (defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || \ - (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS))) - if (port->raddr.addr.ss_family == AF_UNIX) - { -#if defined(HAVE_STRUCT_FCRED) || defined(HAVE_STRUCT_SOCKCRED) - - /* - * Receive credentials on next message receipt, BSD/OS, - * NetBSD. We need to set this before the client sends the - * next packet. - */ - int on = 1; - - if (setsockopt(port->sock, 0, LOCAL_CREDS, &on, sizeof(on)) < 0) - ereport(FATAL, - (errcode_for_socket_access(), - errmsg("could not enable credential reception: %m"))); -#endif - - sendAuthRequest(port, AUTH_REQ_SCM_CREDS); - } -#endif status = auth_peer(port); -#else /* HAVE_UNIX_SOCKETS */ +#else Assert(false); #endif break; @@ -1774,11 +1744,11 @@ ident_inet_done: } /* - * Ask kernel about the credentials of the connecting process and - * determine the symbolic name of the corresponding user. + * Ask kernel about the credentials of the connecting process, + * determine the symbolic name of the corresponding user, and check + * if valid per the usermap. * - * Returns either true and the username put into "ident_user", - * or false if we were unable to determine the username. + * Iff authorized, return STATUS_OK, otherwise return STATUS_ERROR. */ #ifdef HAVE_UNIX_SOCKETS @@ -1786,12 +1756,12 @@ static int auth_peer(hbaPort *port) { char ident_user[IDENT_USERNAME_MAX + 1]; + uid_t uid = 0; + struct passwd *pass; #if defined(HAVE_GETPEEREID) - /* OpenBSD (also Mac OS X) style: use getpeereid() */ - uid_t uid; + /* Most BSDen, including OS X: use getpeereid() */ gid_t gid; - struct passwd *pass; errno = 0; if (getpeereid(port->sock, &uid, &gid) != 0) @@ -1802,23 +1772,10 @@ auth_peer(hbaPort *port) errmsg("could not get peer credentials: %m"))); return STATUS_ERROR; } - - pass = getpwuid(uid); - - if (pass == NULL) - { - ereport(LOG, - (errmsg("local user with ID %d does not exist", - (int) uid))); - return STATUS_ERROR; - } - - strlcpy(ident_user, pass->pw_name, IDENT_USERNAME_MAX + 1); #elif defined(SO_PEERCRED) - /* Linux style: use getsockopt(SO_PEERCRED) */ + /* Linux: use getsockopt(SO_PEERCRED) */ struct ucred peercred; ACCEPT_TYPE_ARG3 so_len = sizeof(peercred); - struct passwd *pass; errno = 0; if (getsockopt(port->sock, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0 || @@ -1830,22 +1787,26 @@ auth_peer(hbaPort *port) errmsg("could not get peer credentials: %m"))); return STATUS_ERROR; } + uid = peercred.uid; +#elif defined(LOCAL_PEERCRED) + /* Debian with FreeBSD kernel: use getsockopt(LOCAL_PEERCRED) */ + struct xucred peercred; + ACCEPT_TYPE_ARG3 so_len = sizeof(peercred); - pass = getpwuid(peercred.uid); - - if (pass == NULL) + errno = 0; + if (getsockopt(port->sock, 0, LOCAL_PEERCRED, &peercred, &so_len) != 0 || + so_len != sizeof(peercred) || + peercred.cr_version != XUCRED_VERSION) { + /* We didn't get a valid credentials struct. */ ereport(LOG, - (errmsg("local user with ID %d does not exist", - (int) peercred.uid))); + (errcode_for_socket_access(), + errmsg("could not get peer credentials: %m"))); return STATUS_ERROR; } - - strlcpy(ident_user, pass->pw_name, IDENT_USERNAME_MAX + 1); + uid = peercred.cr_uid; #elif defined(HAVE_GETPEERUCRED) - /* Solaris > 10: use getpeerucred() */ - uid_t uid; - struct passwd *pass; + /* Solaris: use getpeerucred() */ ucred_t *ucred; ucred = NULL; /* must be initialized to NULL */ @@ -1866,8 +1827,16 @@ auth_peer(hbaPort *port) } ucred_free(ucred); +#else + ereport(LOG, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Peer authentication is not supported on local connections on this platform"))); + + return STATUS_ERROR; +#endif pass = getpwuid(uid); + if (pass == NULL) { ereport(LOG, @@ -1877,90 +1846,6 @@ auth_peer(hbaPort *port) } strlcpy(ident_user, pass->pw_name, IDENT_USERNAME_MAX + 1); -#elif defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS)) - /* Assorted BSDen: use a credentials control message */ -#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 - - struct msghdr msg; - struct cmsghdr *cmsg; - union - { - struct cmsghdr hdr; - unsigned char buf[CMSG_SPACE(sizeof(Cred))]; - } cmsgbuf; - struct iovec iov; - char buf; - Cred *cred; - struct passwd *pw; - - /* - * The one character that is received here is not meaningful; its purpose - * 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; - - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = &cmsgbuf.buf; - msg.msg_controllen = sizeof(cmsgbuf.buf); - memset(&cmsgbuf, 0, sizeof(cmsgbuf)); - - if (recvmsg(port->sock, &msg, 0) < 0) - { - ereport(LOG, - (errcode_for_socket_access(), - errmsg("could not get peer credentials: %m"))); - return STATUS_ERROR; - } - - cmsg = CMSG_FIRSTHDR(&msg); - if (msg.msg_flags & (MSG_TRUNC | MSG_CTRUNC) || - cmsg == NULL || - cmsg->cmsg_len < CMSG_LEN(sizeof(Cred)) || - cmsg->cmsg_level != SOL_SOCKET || - cmsg->cmsg_type != SCM_CREDS) - { - ereport(LOG, - (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("could not get peer credentials: incorrect control message"))); - return STATUS_ERROR; - } - - 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 STATUS_ERROR; - } - - strlcpy(ident_user, pw->pw_name, IDENT_USERNAME_MAX + 1); -#else - ereport(LOG, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("Ident authentication is not supported on local connections on this platform"))); - - return STATUS_ERROR; -#endif return check_usermap(port->hba->usermap, port->user_name, ident_user, false); } diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index 04560c74bf5..5d38f25d263 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -505,9 +505,6 @@ /* Define to 1 if the system has the type `struct cmsgcred'. */ #undef HAVE_STRUCT_CMSGCRED -/* Define to 1 if the system has the type `struct fcred'. */ -#undef HAVE_STRUCT_FCRED - /* Define to 1 if the system has the type `struct option'. */ #undef HAVE_STRUCT_OPTION @@ -532,9 +529,6 @@ /* Define to 1 if the system has the type `struct sockaddr_un'. */ #undef HAVE_STRUCT_SOCKADDR_UN -/* Define to 1 if the system has the type `struct sockcred'. */ -#undef HAVE_STRUCT_SOCKCRED - /* Define to 1 if `tm_zone' is member of `struct tm'. */ #undef HAVE_STRUCT_TM_TM_ZONE @@ -592,6 +586,9 @@ /* Define to 1 if you have the <sys/types.h> header file. */ #undef HAVE_SYS_TYPES_H +/* Define to 1 if you have the <sys/ucred.h> header file. */ +#undef HAVE_SYS_UCRED_H + /* Define to 1 if you have the <sys/un.h> header file. */ #undef HAVE_SYS_UN_H diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32 index 1ecc05604da..54bbb5ae72f 100644 --- a/src/include/pg_config.h.win32 +++ b/src/include/pg_config.h.win32 @@ -404,9 +404,6 @@ /* Define to 1 if the system has the type `struct cmsgcred'. */ /* #undef HAVE_STRUCT_CMSGCRED */ -/* Define to 1 if the system has the type `struct fcred'. */ -/* #undef HAVE_STRUCT_FCRED */ - /* Define to 1 if the system has the type `struct option'. */ //#define HAVE_STRUCT_OPTION 1 @@ -435,9 +432,6 @@ /* Define to 1 if the system has the type `struct sockaddr_un'. */ /* #undef HAVE_STRUCT_SOCKADDR_UN */ -/* Define to 1 if the system has the type `struct sockcred'. */ -/* #undef HAVE_STRUCT_SOCKCRED */ - /* Define to 1 if `tm_zone' is member of `struct tm'. */ /* #undef HAVE_STRUCT_TM_TM_ZONE */ @@ -483,6 +477,9 @@ /* Define to 1 if you have the <sys/types.h> header file. */ #define HAVE_SYS_TYPES_H 1 +/* Define to 1 if you have the <sys/ucred.h> header file. */ +/* #undef HAVE_SYS_UCRED_H */ + /* Define to 1 if you have the <sys/un.h> header file. */ /* #undef HAVE_SYS_UN_H */ diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c index 094926b4e61..9a0317ba4af 100644 --- a/src/interfaces/libpq/fe-auth.c +++ b/src/interfaces/libpq/fe-auth.c @@ -27,11 +27,9 @@ #else #include <unistd.h> #include <fcntl.h> -#include <sys/types.h> #include <sys/param.h> /* for MAXHOSTNAMELEN on most */ #include <sys/socket.h> -#if defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || defined(HAVE_STRUCT_SOCKCRED) -#include <sys/uio.h> +#ifdef HAVE_SYS_UCRED_H #include <sys/ucred.h> #endif #ifndef MAXHOSTNAMELEN @@ -679,27 +677,25 @@ pg_SSPI_startup(PGconn *conn, int use_negotiate) /* * Respond to AUTH_REQ_SCM_CREDS challenge. * - * Note: current backends will not use this challenge if HAVE_GETPEEREID - * or SO_PEERCRED is defined, but pre-7.4 backends might, so compile the - * code anyway. + * Note: this is dead code as of Postgres 9.1, because current backends will + * never send this challenge. But we must keep it as long as libpq needs to + * interoperate with pre-9.1 servers. It is believed to be needed only on + * Debian/kFreeBSD (ie, FreeBSD kernel with Linux userland, so that the + * getpeereid() function isn't provided by libc). */ static int pg_local_sendauth(PGconn *conn) { -#if defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || \ - (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS)) +#ifdef HAVE_STRUCT_CMSGCRED char buf; struct iovec iov; struct msghdr msg; - -#ifdef HAVE_STRUCT_CMSGCRED struct cmsghdr *cmsg; union { struct cmsghdr hdr; unsigned char buf[CMSG_SPACE(sizeof(struct cmsgcred))]; } cmsgbuf; -#endif /* * The backend doesn't care what we send here, but it wants exactly one @@ -713,8 +709,7 @@ pg_local_sendauth(PGconn *conn) msg.msg_iov = &iov; msg.msg_iovlen = 1; -#ifdef HAVE_STRUCT_CMSGCRED - /* FreeBSD needs us to set up a message that will be filled in by kernel */ + /* We must set up a message that will be filled in by kernel */ memset(&cmsgbuf, 0, sizeof(cmsgbuf)); msg.msg_control = &cmsgbuf.buf; msg.msg_controllen = sizeof(cmsgbuf.buf); @@ -722,7 +717,6 @@ pg_local_sendauth(PGconn *conn) cmsg->cmsg_len = CMSG_LEN(sizeof(struct cmsgcred)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_CREDS; -#endif if (sendmsg(conn->sock, &msg, 0) == -1) { diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index f89ceb96642..5a6502fff4d 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -24,6 +24,9 @@ #ifdef HAVE_UCRED_H #include <ucred.h> #endif +#ifdef HAVE_SYS_UCRED_H +#include <sys/ucred.h> +#endif #include "libpq-fe.h" #include "libpq-int.h" @@ -1856,15 +1859,21 @@ keep_going: /* We will come back to here until there is char *startpacket; int packetlen; - if (conn->requirepeer && conn->requirepeer[0]) + /* + * Implement requirepeer check, if requested and it's a + * Unix-domain socket. + */ + if (conn->requirepeer && conn->requirepeer[0] && + IS_AF_UNIX(conn->raddr.addr.ss_family)) { -#if defined(HAVE_GETPEEREID) || defined(SO_PEERCRED) || defined(HAVE_GETPEERUCRED) +#if defined(HAVE_GETPEEREID) || defined(SO_PEERCRED) || defined(LOCAL_PEERCRED) || defined(HAVE_GETPEERUCRED) char pwdbuf[BUFSIZ]; struct passwd pass_buf; struct passwd *pass; uid_t uid; #if defined(HAVE_GETPEEREID) + /* Most BSDen, including OS X: use getpeereid() */ gid_t gid; errno = 0; @@ -1876,6 +1885,7 @@ keep_going: /* We will come back to here until there is goto error_return; } #elif defined(SO_PEERCRED) + /* Linux: use getsockopt(SO_PEERCRED) */ struct ucred peercred; ACCEPT_TYPE_ARG3 so_len = sizeof(peercred); @@ -1890,7 +1900,25 @@ keep_going: /* We will come back to here until there is goto error_return; } uid = peercred.uid; +#elif defined(LOCAL_PEERCRED) + /* Debian with FreeBSD kernel: use LOCAL_PEERCRED */ + struct xucred peercred; + ACCEPT_TYPE_ARG3 so_len = sizeof(peercred); + + errno = 0; + if (getsockopt(conn->sock, 0, LOCAL_PEERCRED, + &peercred, &so_len) != 0 || + so_len != sizeof(peercred) || + peercred.cr_version != XUCRED_VERSION) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not get peer credentials: %s\n"), + pqStrerror(errno, sebuf, sizeof(sebuf))); + goto error_return; + } + uid = peercred.cr_uid; #elif defined(HAVE_GETPEERUCRED) + /* Solaris: use getpeerucred() */ ucred_t *ucred; ucred = NULL; /* must be initialized to NULL */ |