Implement getpeereid() as a src/port compatibility function.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 2 Jun 2011 17:05:01 +0000 (13:05 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 2 Jun 2011 17:05:01 +0000 (13:05 -0400)
This unifies a bunch of ugly #ifdef's in one place.  Per discussion,
we only need this where HAVE_UNIX_SOCKETS, so no need to cover Windows.

Marko Kreen, some adjustment by Tom Lane

configure
configure.in
src/backend/libpq/auth.c
src/include/port.h
src/interfaces/libpq/fe-connect.c
src/port/getpeereid.c [new file with mode: 0644]

index 5c6022903de79a468985813bbf3331d443cf24f6..a8c59231183584bdd5b2b85f570057f8a3ec3b84 100755 (executable)
--- a/configure
+++ b/configure
@@ -18852,8 +18852,7 @@ fi
 
 
 
-
-for ac_func in cbrt dlopen fcvt fdatasync getifaddrs getpeereid getpeerucred getrlimit memmove poll pstat readlink scandir setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs wcstombs_l
+for ac_func in cbrt dlopen fcvt fdatasync getifaddrs getpeerucred getrlimit memmove poll pstat readlink scandir setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs wcstombs_l
 do
 as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
 { $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
@@ -20424,7 +20423,8 @@ LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
 
 
 
-for ac_func in crypt erand48 getopt getrusage inet_aton random rint srandom strdup strerror strlcat strlcpy strtol strtoul
+
+for ac_func in crypt erand48 getopt getpeereid getrusage inet_aton random rint srandom strdup strerror strlcat strlcpy strtol strtoul
 do
 as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
 { $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
index ed2d17d21988b421877dd40f5517521c0cc3e458..6a7278aefe7416c118e4597d4b6f3bd891840368 100644 (file)
@@ -1191,7 +1191,7 @@ PGAC_VAR_INT_TIMEZONE
 AC_FUNC_ACCEPT_ARGTYPES
 PGAC_FUNC_GETTIMEOFDAY_1ARG
 
-AC_CHECK_FUNCS([cbrt dlopen fcvt fdatasync getifaddrs getpeereid getpeerucred getrlimit memmove poll pstat readlink scandir setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs wcstombs_l])
+AC_CHECK_FUNCS([cbrt dlopen fcvt fdatasync getifaddrs getpeerucred getrlimit memmove poll pstat readlink scandir setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs wcstombs_l])
 
 AC_REPLACE_FUNCS(fseeko)
 case $host_os in
@@ -1310,7 +1310,7 @@ fi
 pgac_save_LIBS="$LIBS"
 LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
 
-AC_REPLACE_FUNCS([crypt erand48 getopt getrusage inet_aton random rint srandom strdup strerror strlcat strlcpy strtol strtoul])
+AC_REPLACE_FUNCS([crypt erand48 getopt getpeereid getrusage inet_aton random rint srandom strdup strerror strlcat strlcpy strtol strtoul])
 
 case $host_os in
 
index dc7ad2cadf49ff7bb83d423602253b7b9f4ee29d..f4099ac23458787d6af8aa2ff82da8130fc7ddcb 100644 (file)
 
 #include <sys/param.h>
 #include <sys/socket.h>
-#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>
@@ -1756,85 +1750,25 @@ static int
 auth_peer(hbaPort *port)
 {
    char        ident_user[IDENT_USERNAME_MAX + 1];
-   uid_t       uid = 0;
-   struct passwd *pass;
-
-#if defined(HAVE_GETPEEREID)
-   /* Most BSDen, including OS X: use getpeereid() */
+   uid_t       uid;
    gid_t       gid;
+   struct passwd *pass;
 
    errno = 0;
    if (getpeereid(port->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 STATUS_ERROR;
-   }
-#elif defined(SO_PEERCRED)
-   /* Linux: use getsockopt(SO_PEERCRED) */
-   struct ucred peercred;
-   ACCEPT_TYPE_ARG3 so_len = sizeof(peercred);
-
-   errno = 0;
-   if (getsockopt(port->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 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);
-
-   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,
-               (errcode_for_socket_access(),
-                errmsg("could not get peer credentials: %m")));
-       return STATUS_ERROR;
-   }
-   uid = peercred.cr_uid;
-#elif defined(HAVE_GETPEERUCRED)
-   /* Solaris: use getpeerucred() */
-   ucred_t    *ucred;
-
-   ucred = NULL;               /* must be initialized to NULL */
-   if (getpeerucred(port->sock, &ucred) == -1)
-   {
-       ereport(LOG,
-               (errcode_for_socket_access(),
-                errmsg("could not get peer credentials: %m")));
-       return STATUS_ERROR;
-   }
-
-   if ((uid = ucred_geteuid(ucred)) == -1)
-   {
-       ereport(LOG,
-               (errcode_for_socket_access(),
-          errmsg("could not get effective UID from peer credentials: %m")));
+       /* Provide special error message if getpeereid is a stub */
+       if (errno == ENOSYS)
+           ereport(LOG,
+                   (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                    errmsg("peer authentication is not supported on this platform")));
+       else
+           ereport(LOG,
+                   (errcode_for_socket_access(),
+                    errmsg("could not get peer credentials: %m")));
        return STATUS_ERROR;
    }
 
-   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)
index 6ea681f16ace64ad0d035bea97f9c839f84149e0..d7295e3132798f9d506ff653677c6f816f1e4bc9 100644 (file)
@@ -395,6 +395,10 @@ extern void srand48(long seed);
 extern int getopt(int nargc, char *const * nargv, const char *ostr);
 #endif
 
+#ifndef HAVE_GETPEEREID
+extern int getpeereid(int sock, uid_t *uid, gid_t *gid);
+#endif
+
 #ifndef HAVE_ISINF
 extern int isinf(double x);
 #endif
index 5a6502fff4dab16df82ec8767313f8fa351ed58a..9aa6ca01eb2fb5c325e50963e495789635754556 100644 (file)
 #include <ctype.h>
 #include <time.h>
 #include <unistd.h>
-#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"
@@ -1859,6 +1853,7 @@ keep_going:                       /* We will come back to here until there is
                char       *startpacket;
                int         packetlen;
 
+#ifdef HAVE_UNIX_SOCKETS
                /*
                 * Implement requirepeer check, if requested and it's a
                 * Unix-domain socket.
@@ -1866,82 +1861,25 @@ keep_going:                     /* We will come back to here until there is
                if (conn->requirepeer && conn->requirepeer[0] &&
                    IS_AF_UNIX(conn->raddr.addr.ss_family))
                {
-#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;
                    if (getpeereid(conn->sock, &uid, &gid) != 0)
                    {
-                       appendPQExpBuffer(&conn->errorMessage,
-                       libpq_gettext("could not get peer credentials: %s\n"),
-                                   pqStrerror(errno, sebuf, sizeof(sebuf)));
-                       goto error_return;
-                   }
-#elif defined(SO_PEERCRED)
-                   /* Linux: use getsockopt(SO_PEERCRED) */
-                   struct ucred peercred;
-                   ACCEPT_TYPE_ARG3 so_len = sizeof(peercred);
-
-                   errno = 0;
-                   if (getsockopt(conn->sock, SOL_SOCKET, SO_PEERCRED,
-                                  &peercred, &so_len) != 0 ||
-                       so_len != sizeof(peercred))
-                   {
-                       appendPQExpBuffer(&conn->errorMessage,
-                       libpq_gettext("could not get peer credentials: %s\n"),
-                                   pqStrerror(errno, sebuf, sizeof(sebuf)));
-                       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 */
-                   if (getpeerucred(conn->sock, &ucred) == -1)
-                   {
-                       appendPQExpBuffer(&conn->errorMessage,
-                       libpq_gettext("could not get peer credentials: %s\n"),
-                                   pqStrerror(errno, sebuf, sizeof(sebuf)));
-                       goto error_return;
-                   }
-
-                   if ((uid = ucred_geteuid(ucred)) == -1)
-                   {
-                       appendPQExpBuffer(&conn->errorMessage,
-                                         libpq_gettext("could not get effective UID from peer credentials: %s\n"),
-                                   pqStrerror(errno, sebuf, sizeof(sebuf)));
-                       ucred_free(ucred);
+                       /* Provide special error message if getpeereid is a stub */
+                       if (errno == ENOSYS)
+                           appendPQExpBuffer(&conn->errorMessage,
+                                             libpq_gettext("requirepeer parameter is not supported on this platform\n"));
+                       else
+                           appendPQExpBuffer(&conn->errorMessage,
+                                             libpq_gettext("could not get peer credentials: %s\n"),
+                                             pqStrerror(errno, sebuf, sizeof(sebuf)));
                        goto error_return;
                    }
-                   ucred_free(ucred);
-#else
-#error missing implementation method for requirepeer
-#endif
 
                    pqGetpwuid(uid, &pass_buf, pwdbuf, sizeof(pwdbuf), &pass);
 
@@ -1960,12 +1898,8 @@ keep_going:                      /* We will come back to here until there is
                                          conn->requirepeer, pass->pw_name);
                        goto error_return;
                    }
-#else                          /* can't support requirepeer */
-                   appendPQExpBuffer(&conn->errorMessage,
-                                     libpq_gettext("requirepeer parameter is not supported on this platform\n"));
-                   goto error_return;
-#endif
                }
+#endif /* HAVE_UNIX_SOCKETS */
 
 #ifdef USE_SSL
 
diff --git a/src/port/getpeereid.c b/src/port/getpeereid.c
new file mode 100644 (file)
index 0000000..3b826f0
--- /dev/null
@@ -0,0 +1,80 @@
+/*-------------------------------------------------------------------------
+ *
+ * getpeereid.c
+ *     get peer userid for UNIX-domain socket connection
+ *
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *   src/port/getpeereid.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "c.h"
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#ifdef HAVE_UCRED_H
+#include <ucred.h>
+#endif
+#ifdef HAVE_SYS_UCRED_H
+#include <sys/ucred.h>
+#endif
+
+
+/*
+ * BSD-style getpeereid() for platforms that lack it.
+ */
+int
+getpeereid(int sock, uid_t *uid, gid_t *gid)
+{
+#if defined(SO_PEERCRED)
+   /* Linux: use getsockopt(SO_PEERCRED) */
+   struct ucred peercred;
+   ACCEPT_TYPE_ARG3 so_len = sizeof(peercred);
+
+   if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0 ||
+       so_len != sizeof(peercred))
+       return -1;
+   *uid = peercred.uid;
+   *gid = peercred.gid;
+   return 0;
+#elif defined(LOCAL_PEERCRED)
+   /* Debian with FreeBSD kernel: use getsockopt(LOCAL_PEERCRED) */
+   struct xucred peercred;
+   ACCEPT_TYPE_ARG3 so_len = sizeof(peercred);
+
+   if (getsockopt(sock, 0, LOCAL_PEERCRED, &peercred, &so_len) != 0 ||
+       so_len != sizeof(peercred) ||
+       peercred.cr_version != XUCRED_VERSION)
+       return -1;
+   *uid = peercred.cr_uid;
+   *gid = peercred.cr_gid;
+   return 0;
+#elif defined(HAVE_GETPEERUCRED)
+   /* Solaris: use getpeerucred() */
+   ucred_t    *ucred;
+
+   ucred = NULL;               /* must be initialized to NULL */
+   if (getpeerucred(sock, &ucred) == -1)
+       return -1;
+
+   *uid = ucred_geteuid(ucred);
+   *gid = ucred_getegid(ucred);
+   ucred_free(ucred);
+
+   if (*uid == (pid_t)(-1) || *gid == (gid_t)(-1))
+       return -1;
+   return 0;
+#else
+   /* No implementation available on this platform */
+   errno = ENOSYS;
+   return -1;
+#endif
+}