Patch: use SCM_CREDS authentication over PF_LOCAL sockets - Mailing list pgsql-patches

From wollman@LCS.MIT.EDU
Subject Patch: use SCM_CREDS authentication over PF_LOCAL sockets
Date
Msg-id 200108121917.f7CJHgN55360@mintaka.lcs.mit.edu
Whole thread Raw
Responses Re: Patch: use SCM_CREDS authentication over PF_LOCAL sockets  (Tom Lane <tgl@sss.pgh.pa.us>)
Re: Patch: use SCM_CREDS authentication over PF_LOCAL sockets  (Bruce Momjian <pgman@candle.pha.pa.us>)
List pgsql-patches
The following set of patches (relative to 7.1.2 release) implement
SCM_CREDS authentication for local connections.  On systems which
support it, this mechanism should be used instead of `trust' for local
connections.

-GAWollman

------------------------------------------------------------------------
--- src/backend/libpq/auth.c    Wed Mar 21 22:59:30 2001
+++ src/backend/libpq/auth.c    Sun Aug 12 14:46:35 2001
@@ -26,6 +26,11 @@
 #include <ctype.h>

 #include <sys/types.h>            /* needed by in.h on Ultrix */
+#include <sys/socket.h>            /* for SCM_CREDS */
+#ifdef SCM_CREDS
+#include <sys/uio.h>            /* for struct iovec */
+#include <errno.h>
+#endif
 #include <netinet/in.h>
 #include <arpa/inet.h>

@@ -42,6 +47,7 @@
 static int    handle_done_auth(void *arg, PacketLen len, void *pkt);
 static int    handle_krb4_auth(void *arg, PacketLen len, void *pkt);
 static int    handle_krb5_auth(void *arg, PacketLen len, void *pkt);
+static int    handle_local_auth(void *arg, PacketLen len, void *pkt);
 static int    handle_password_auth(void *arg, PacketLen len, void *pkt);
 static int    readPasswordPacket(void *arg, PacketLen len, void *pkt);
 static int    pg_passwordv0_recvauth(void *arg, PacketLen len, void *pkt);
@@ -322,6 +328,98 @@
 #endif     /* KRB5 */


+#ifdef SCM_CREDS
+static int
+pg_local_recvauth(Port *port)
+{
+    struct msghdr msg;
+    struct {
+        struct cmsghdr hdr;
+        struct cmsgcred cred;
+    } cmsg;
+    struct iovec iov;
+    char buf;
+    char namebuf[SM_USER + 1];
+    struct passwd *pw;
+
+    msg.msg_name = NULL;
+    msg.msg_namelen = 0;
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+    msg.msg_control = (char *)&cmsg;
+    msg.msg_controllen = sizeof cmsg;
+    msg.msg_flags = 0;
+
+    /*
+     * 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(port->sock, &msg, 0) < 0) {
+        snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+             "pg_local_recvauth: error receiving credentials: %s\n",
+             strerror(errno));
+errout:
+        fputs(PQerrormsg, stderr);
+        pqdebug("%s", PQerrormsg);
+
+        return STATUS_ERROR;
+    }
+
+    /*
+     * Make sure we got the right kind of message.
+     */
+    if (cmsg.hdr.cmsg_len != sizeof cmsg
+        || cmsg.hdr.cmsg_level != SOL_SOCKET
+        || cmsg.hdr.cmsg_type != SCM_CREDS) {
+        snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+             "pg_local_recvauth: protocol error receiving credentials\n");
+        goto errout;
+    }
+
+    snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+         "pg_local_recvauth: pid %lu, uid %lu\n",
+         (unsigned long)cmsg.cred.cmcred_pid,
+         (unsigned long)cmsg.cred.cmcred_uid);
+    pqdebug("%s", PQerrormsg);
+
+    strncpy(namebuf, port->user, SM_USER);
+    namebuf[SM_USER] = '\0';
+
+    pw = getpwnam(namebuf);
+    if (pw == NULL) {
+        snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+             "pg_local_recvauth: unknown local user %s\n",
+             namebuf);
+        goto errout;
+    }
+
+    if (pw->pw_uid != cmsg.cred.cmcred_uid) {
+        snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+             "pg_local_recvauth: %s's uid %lu != real uid %lu\n",
+             namebuf, (unsigned long)pw->pw_uid,
+             (unsigned long)cmsg.cred.cmcred_uid);
+        goto errout;
+    }
+    return STATUS_OK;
+}
+
+#else
+static int
+pg_local_recvauth(Port *port)
+{
+    snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+         "pg_local_recvauth: credential passing not implemented on this server.\n");
+    fputs(PQerrormsg, stderr);
+    pqdebug("%s", PQerrormsg);
+
+    return STATUS_ERROR;
+}
+#endif /* SCM_CREDS */
+
 /*
  * Handle a v0 password packet.
  */
@@ -439,6 +537,9 @@
         case uaCrypt:
             authmethod = "Password";
             break;
+        case uaLocalCred:
+            authmethod = "Local credential";
+            break;
     }

     sprintf(buffer, "%s authentication failed for user '%s'",
@@ -545,6 +646,10 @@
                 areq = AUTH_REQ_CRYPT;
                 auth_handler = handle_password_auth;
                 break;
+            case uaLocalCred:
+                areq = AUTH_REQ_LOCALCRED;
+                auth_handler = handle_local_auth;
+                break;
         }

         /* Tell the frontend what we want next. */
@@ -668,6 +773,24 @@


 /*
+ * Called when we have told the front end that it should use local
+ * credential authentication.
+ */
+
+static int
+handle_local_auth(void *arg, PacketLen len, void *pkt)
+{
+    Port       *port = (Port *) arg;
+
+    if (pg_local_recvauth(port) != STATUS_OK)
+        auth_failed(port);
+    else
+        sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth);
+
+    return STATUS_OK;
+}
+
+/*
  * Called when we have received the password packet.
  */

@@ -777,6 +900,10 @@

         case uaTrust:
             status = STATUS_OK;
+            break;
+
+        case uaLocalCred: /* can't happen */
+            status = STATUS_ERROR;
             break;

         case uaIdent:
--- src/backend/libpq/hba.c    Fri Feb  9 21:31:26 2001
+++ src/backend/libpq/hba.c    Sun Aug 12 14:10:28 2001
@@ -125,6 +125,8 @@
         *userauth_p = uaReject;
     else if (strcmp(buf, "crypt") == 0)
         *userauth_p = uaCrypt;
+    else if (strcmp(buf, "local") == 0)
+        *userauth_p = uaLocalCred;
     else
     {
         *error_p = true;
@@ -279,6 +281,14 @@
          */

         read_hba_entry2(file, &port->auth_method, port->auth_arg, error_p);
+
+        /*
+         * Disallow authentication methods which require a PF_LOCAL
+         * socket.
+         */
+        if (!*error_p &&
+            (port->auth_method == uaLocalCred))
+            *error_p = true;

         if (*error_p)
             goto syntax;
--- src/backend/libpq/pg_hba.conf.sample    Tue Nov 21 15:44:32 2000
+++ src/backend/libpq/pg_hba.conf.sample    Sun Aug 12 14:17:41 2001
@@ -96,6 +96,12 @@
 #   trust:      No authentication is done. Trust that the user has the
 #           authority to use whatever username he specifies.
 #
+#   local:    Use the credential-passing feature of local-domain sockets
+#        (available only on certain operating systems) to
+#        authenticate the user.  Allow the user access if his
+#        real UID matches the UID in the system password file
+#        for the requested username.
+#
 #   password:    Authentication is done by matching a password supplied
 #           in clear by the host. If AUTH_ARGUMENT is specified then
 #           the password is compared with the user's entry in that
@@ -123,7 +129,7 @@
 #
 #   reject:     Reject the connection.
 #
-# Local (UNIX socket) connections support only AUTHTYPEs "trust",
+# Local (UNIX socket) connections support only AUTHTYPEs "trust", "local",
 # "password", "crypt", and "reject".


@@ -137,9 +143,18 @@
 #
 # host       all         127.0.0.1     255.255.255.255    trust
 #
-# The same, over Unix-socket connections:
+# Allow any user to connect to any database over a local socket,
+# provided that the user's real UID is the same as the requested
+# database username's UID:
+#
+# local      all                                          local
+#
+# On operating systems where credential passing over local sockets
+# is not available, allow any user to connect to any database over
+# a local socket under any username:
 #
 # local      all                                          trust
+#
 #
 # Allow any user from any host with IP address 192.168.93.x to
 # connect to database "template1" as the same username that ident on that
--- src/include/libpq/hba.h    Wed Mar 21 23:00:47 2001
+++ src/include/libpq/hba.h    Sun Aug 12 13:33:30 2001
@@ -35,7 +35,8 @@
     uaTrust,
     uaIdent,
     uaPassword,
-    uaCrypt
+    uaCrypt,
+    uaLocalCred
 } UserAuth;

 typedef struct Port hbaPort;
--- src/include/libpq/pqcomm.h    Wed Mar 21 23:00:48 2001
+++ src/include/libpq/pqcomm.h    Sun Aug 12 13:35:16 2001
@@ -90,7 +90,7 @@
 /* The earliest and latest frontend/backend protocol version supported. */

 #define PG_PROTOCOL_EARLIEST    PG_PROTOCOL(0,0)
-#define PG_PROTOCOL_LATEST    PG_PROTOCOL(2,0)
+#define PG_PROTOCOL_LATEST    PG_PROTOCOL(2,1)

 /*
  * All packets sent to the postmaster start with the length.  This is omitted
@@ -132,6 +132,7 @@
 #define AUTH_REQ_KRB5        2    /* Kerberos V5 */
 #define AUTH_REQ_PASSWORD    3    /* Password */
 #define AUTH_REQ_CRYPT        4    /* Encrypted password */
+#define    AUTH_REQ_LOCALCRED    5    /* PF_LOCAL credentials */

 typedef uint32 AuthRequest;

--- src/interfaces/libpq/fe-auth.c    Wed Mar 21 23:01:25 2001
+++ src/interfaces/libpq/fe-auth.c    Sun Aug 12 14:50:44 2001
@@ -43,6 +43,11 @@
 #ifndef  MAXHOSTNAMELEN
 #include <netdb.h>                /* for MAXHOSTNAMELEN on some */
 #endif
+#include <sys/socket.h>        /* for SCM_CREDS */
+#ifdef SCM_CREDS
+#include <sys/uio.h>
+#include <sys/errno.h>
+#endif
 #include <pwd.h>
 #endif

@@ -430,6 +435,52 @@

 #endif     /* KRB5 */

+#ifdef SCM_CREDS
+static int
+pg_local_sendauth(char *PQerrormsg, PGconn *conn)
+{
+    char buf;
+    struct iovec iov;
+    struct {
+        struct cmsghdr hdr;
+        struct cmsgcred cred;
+    } cmsg;
+    struct msghdr msg;
+
+    /*
+     * The backend doesn't care what we send here, but it wants
+     * exactly one character to force recvmsg() to block and wait
+     * for us.
+     */
+    buf = '\0';
+    iov.iov_base = &buf;
+    iov.iov_len = 1;
+
+    cmsg.hdr.cmsg_len = sizeof cmsg;
+    cmsg.hdr.cmsg_level = SOL_SOCKET;
+    cmsg.hdr.cmsg_type = SCM_CREDS;
+    /*
+     * cmsg.cred will get filled in with the correct information
+     * by the kernel when this message is sent.
+     */
+
+    msg.msg_name = NULL;
+    msg.msg_namelen = 0;
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+    msg.msg_control = &cmsg;
+    msg.msg_controllen = sizeof cmsg;
+    msg.msg_flags = 0;
+
+    if (sendmsg(conn->sock, &msg, 0) == -1) {
+        snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+             "pg_local_sendauth: sendmsg: %s", strerror(errno));
+        return STATUS_ERROR;
+    }
+    return STATUS_OK;
+}
+#endif
+
 static int
 pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
 {
@@ -507,6 +558,17 @@
             }

             break;
+
+        case AUTH_REQ_LOCALCRED:
+#ifdef SCM_CREDS
+            if (pg_local_sendauth(PQerrormsg, conn) != STATUS_OK)
+                return STATUS_ERROR;
+            break;
+#else
+            (void) sprintf(PQerrormsg,
+                     "fe_sendauth: local authentication not supported\n");
+            return STATUS_ERROR;
+#endif

         default:
             (void) sprintf(PQerrormsg,

pgsql-patches by date:

Previous
From: Paul Ramsey
Date:
Subject: Re: contrib/postgis spatial extensions
Next
From: Tom Lane
Date:
Subject: Re: Patch: use SCM_CREDS authentication over PF_LOCAL sockets