aboutsummaryrefslogtreecommitdiff
path: root/src/interfaces/libpq/fe-connect.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2011-05-31 16:10:46 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2011-05-31 16:10:46 -0400
commitbe4585b1c27ac5dbdd0d61740d18f7ad9a00e268 (patch)
tree8d3b5db3a184ccc81d86e771b9ad99c095b89f97 /src/interfaces/libpq/fe-connect.c
parent13c00ae8c73ee9635c11059925814b351dc3593c (diff)
downloadpostgresql-be4585b1c27ac5dbdd0d61740d18f7ad9a00e268.tar.gz
postgresql-be4585b1c27ac5dbdd0d61740d18f7ad9a00e268.zip
Replace use of credential control messages with getsockopt(LOCAL_PEERCRED).
It turns out the reason we hadn't found out about the portability issues with our credential-control-message code is that almost no modern platforms use that code at all; the ones that used to need it now offer getpeereid(), which we choose first. The last holdout was NetBSD, and they added getpeereid() as of 5.0. So far as I can tell, the only live platform on which that code was being exercised was Debian/kFreeBSD, ie, FreeBSD kernel with Linux userland --- since glibc doesn't provide getpeereid(), we fell back to the control message code. However, the FreeBSD kernel provides a LOCAL_PEERCRED socket parameter that's functionally equivalent to Linux's SO_PEERCRED. That is both much simpler to use than control messages, and superior because it doesn't require receiving a message from the other end at just the right time. Therefore, add code to use LOCAL_PEERCRED when necessary, and rip out all the credential-control-message code in the backend. (libpq still has such code so that it can still talk to pre-9.1 servers ... but eventually we can get rid of it there too.) Clean up related autoconf probes, too. This means that libpq's requirepeer parameter now works on exactly the same platforms where the backend supports peer authentication, so adjust the documentation accordingly.
Diffstat (limited to 'src/interfaces/libpq/fe-connect.c')
-rw-r--r--src/interfaces/libpq/fe-connect.c32
1 files changed, 30 insertions, 2 deletions
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 */