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

From Bruce Momjian
Subject Re: Patch: use SCM_CREDS authentication over PF_LOCAL sockets
Date
Msg-id 200108160434.f7G4YE527548@candle.pha.pa.us
Whole thread Raw
In response to Patch: use SCM_CREDS authentication over PF_LOCAL sockets  (wollman@LCS.MIT.EDU)
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.

OK, here is a cleaned up version of the patch that will apply to current
CVS.  I worked it into the SO_PEERCRED code.  I made some changes so it
compiles on BSD/OS.  I am getting "Invalid Argument" from libpq's
sending of the credentials on BSD/OS.  I would be interested to know if
this works on FreeBSD.  Solaris uses this capability too.

Also, we are not updating the protocol version, so I hope it fails
gracefully on old clients.

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 853-3000
  +  If your life is a hard drive,     |  830 Blythe Avenue
  +  Christ can be your backup.        |  Drexel Hill, Pennsylvania 19026
Index: src/backend/libpq/auth.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/auth.c,v
retrieving revision 1.57
diff -c -r1.57 auth.c
*** src/backend/libpq/auth.c    2001/08/15 18:42:14    1.57
--- src/backend/libpq/auth.c    2001/08/16 03:41:10
***************
*** 15,24 ****

  #include "postgres.h"

! #include <sys/types.h>            /* needed by in.h on Ultrix */
  #include <netinet/in.h>
  #include <arpa/inet.h>
-
  #include "libpq/auth.h"
  #include "libpq/crypt.h"
  #include "libpq/hba.h"
--- 15,29 ----

  #include "postgres.h"

! #include <sys/types.h>
! #include <sys/socket.h>            /* for SCM_CREDS */
! #ifdef SCM_CREDS
! #include <sys/uio.h>            /* for struct iovec */
! #include <sys/ucred.h>
! #include <errno.h>
! #endif
  #include <netinet/in.h>
  #include <arpa/inet.h>
  #include "libpq/auth.h"
  #include "libpq/crypt.h"
  #include "libpq/hba.h"
***************
*** 28,39 ****
  #include "miscadmin.h"

  static void sendAuthRequest(Port *port, AuthRequest areq);
-
  static int    checkPassword(Port *port, char *user, char *password);
  static int    old_be_recvauth(Port *port);
  static int    map_old_to_new(Port *port, UserAuth old, int status);
  static void auth_failed(Port *port);
-
  static int    recv_and_check_password_packet(Port *port);
  static int    recv_and_check_passwordv0(Port *port);

--- 33,42 ----
***************
*** 493,498 ****
--- 496,507 ----
              break;

          case uaIdent:
+ #ifdef SCM_CREDS
+             /* If we are doing ident on unix-domain sockets,
+                we are going to use SCM_CREDS, if defined. */
+             if (port->raddr.sa.sa_family ==    AF_UNIX)
+                 sendAuthRequest(port, AUTH_REQ_SCM_CREDS);
+ #endif
              status = authident(port);
              break;

Index: src/backend/libpq/hba.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/hba.c,v
retrieving revision 1.62
diff -c -r1.62 hba.c
*** src/backend/libpq/hba.c    2001/08/15 18:42:15    1.62
--- src/backend/libpq/hba.c    2001/08/16 03:41:10
***************
*** 19,24 ****
--- 19,30 ----
  #include <errno.h>
  #include <pwd.h>
  #include <sys/types.h>
+ #include <sys/socket.h>            /* for SCM_CREDS */
+ #ifdef SCM_CREDS
+ #include <sys/uio.h>            /* for struct iovec */
+ #include <sys/ucred.h>
+ #include <errno.h>
+ #endif
  #include <fcntl.h>
  #include <sys/socket.h>
  #include <netinet/in.h>
***************
*** 870,876 ****
  static bool
  ident_unix(int sock, char *ident_user)
  {
! #ifdef SO_PEERCRED
      /* Linux style: use getsockopt(SO_PEERCRED) */
      struct ucred    peercred;
      ACCEPT_TYPE_ARG3 so_len = sizeof(peercred);
--- 876,966 ----
  static bool
  ident_unix(int sock, char *ident_user)
  {
! #ifdef SCM_CREDS
!     struct msghdr msg;
!     struct {
!         struct cmsghdr hdr;
! #ifndef fc_uid
!         struct cmsgcred cred;
! #define cruid cmcred_uid
! #else
!         struct fcred cred;
! #define cruid fc_uid
! #endif
!     } 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(sock, &msg, 0) < 0) {
!         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
!              "ident_unix: error receiving credentials: %s\n",
!              strerror(errno));
! errout:
!         fputs(PQerrormsg, stderr);
!         pqdebug("%s", PQerrormsg);
!
!         return false;
!     }
!
!     /*
!      * 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,
!              "ident_unix: protocol error receiving credentials\n");
!         goto errout;
!     }
!
!     snprintf(PQerrormsg, PQERRORMSG_LENGTH,
!          "ident_unix: pid %lu, uid %lu\n",
! #ifndef fc_uid
!          (unsigned long)cmsg.cred.cmcred_pid,
! #else
!          (unsigned long)0, /* unavailable */
! #endif
!          (unsigned long)cmsg.cred.cruid);
!     pqdebug("%s", PQerrormsg);
!
!     strncpy(namebuf, ident_user, SM_USER);
!     namebuf[SM_USER] = '\0';
!
!     pw = getpwnam(namebuf);
!     if (pw == NULL) {
!         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
!              "ident_unix: unknown local user %s\n",
!              namebuf);
!         goto errout;
!     }
!
!     if (pw->pw_uid != cmsg.cred.cruid) {
!         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
!              "ident_unix: %s's uid %lu != real uid %lu\n",
!              namebuf, (unsigned long)pw->pw_uid,
!              (unsigned long)cmsg.cred.cruid);
!         goto errout;
!     }
!     return true;
! #elif SO_PEERCRED
      /* Linux style: use getsockopt(SO_PEERCRED) */
      struct ucred    peercred;
      ACCEPT_TYPE_ARG3 so_len = sizeof(peercred);
***************
*** 906,912 ****

      return true;

! #else /* not SO_PEERCRED */

      snprintf(PQerrormsg, PQERRORMSG_LENGTH,
               "IDENT auth is not supported on local connections on this platform\n");
--- 996,1002 ----

      return true;

! #else

      snprintf(PQerrormsg, PQERRORMSG_LENGTH,
               "IDENT auth is not supported on local connections on this platform\n");
***************
*** 914,920 ****
      pqdebug("%s", PQerrormsg);
      return false;

! #endif /* SO_PEERCRED */
  }

  /*
--- 1004,1010 ----
      pqdebug("%s", PQerrormsg);
      return false;

! #endif
  }

  /*
Index: src/backend/libpq/pg_hba.conf.sample
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/pg_hba.conf.sample,v
retrieving revision 1.24
diff -c -r1.24 pg_hba.conf.sample
*** src/backend/libpq/pg_hba.conf.sample    2001/08/15 18:42:15    1.24
--- src/backend/libpq/pg_hba.conf.sample    2001/08/16 03:41:13
***************
*** 125,136 ****
  #   ident:    For TCP/IP connections, authentication is done by contacting
  #        the ident server on the client host.  (CAUTION: this is only
  #        as secure as the client machine!)  On machines that support
! #        SO_PEERCRED socket requests, this method also works for
! #        local Unix-domain connections.  AUTH_ARGUMENT is required:
! #        it determines how to map remote user names to Postgres user
! #        names.  The AUTH_ARGUMENT is a map name found in the
! #        $PGDATA/pg_ident.conf file. The connection is accepted if
! #        that file contains an entry for this map name with the
  #        ident-supplied username and the requested Postgres username.
  #        The special map name "sameuser" indicates an implied map
  #        (not in pg_ident.conf) that maps each ident username to the
--- 125,136 ----
  #   ident:    For TCP/IP connections, authentication is done by contacting
  #        the ident server on the client host.  (CAUTION: this is only
  #        as secure as the client machine!)  On machines that support
! #        SO_PEERCRED or SCM_CREDS socket requests, this method also
! #        works for local Unix-domain connections.  AUTH_ARGUMENT is
! #        required: it determines how to map remote user names to
! #        Postgres user names.  The AUTH_ARGUMENT is a map name found
! #        in the $PGDATA/pg_ident.conf file. The connection is accepted
! #        if that file contains an entry for this map name with the
  #        ident-supplied username and the requested Postgres username.
  #        The special map name "sameuser" indicates an implied map
  #        (not in pg_ident.conf) that maps each ident username to the
Index: src/include/libpq/pqcomm.h
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/include/libpq/pqcomm.h,v
retrieving revision 1.56
diff -c -r1.56 pqcomm.h
*** src/include/libpq/pqcomm.h    2001/08/15 18:42:15    1.56
--- src/include/libpq/pqcomm.h    2001/08/16 03:41:14
***************
*** 133,138 ****
--- 133,139 ----
  #define AUTH_REQ_PASSWORD    3    /* Password */
  #define AUTH_REQ_CRYPT        4    /* crypt password */
  #define AUTH_REQ_MD5        5    /* md5 password */
+ #define AUTH_REQ_SCM_CREDS    6    /* transfer SCM credentials */

  typedef uint32 AuthRequest;

Index: src/interfaces/libpq/fe-auth.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v
retrieving revision 1.50
diff -c -r1.50 fe-auth.c
*** src/interfaces/libpq/fe-auth.c    2001/08/15 21:08:21    1.50
--- src/interfaces/libpq/fe-auth.c    2001/08/16 03:41:14
***************
*** 40,50 ****
--- 40,57 ----
  #else
  #include <unistd.h>
  #include <fcntl.h>
+ #ifdef SCM_CREDS
+ #include <sys/uio.h>            /* for struct iovec */
+ #include <sys/ucred.h>
+ #include <errno.h>
+ #endif
  #include <sys/param.h>            /* for MAXHOSTNAMELEN on most */
  #ifndef  MAXHOSTNAMELEN
  #include <netdb.h>                /* for MAXHOSTNAMELEN on some */
  #endif
  #include <pwd.h>
+ #include <sys/types.h>
+ #include <sys/socket.h>            /* for SCM_CREDS */
  #endif

  #ifdef HAVE_CRYPT_H
***************
*** 432,437 ****
--- 439,495 ----

  #endif     /* KRB5 */

+ #ifdef SCM_CREDS
+ static int
+ pg_local_sendauth(char *PQerrormsg, PGconn *conn)
+ {
+     char buf;
+     struct iovec iov;
+     struct {
+         struct cmsghdr hdr;
+ #ifndef fc_uid
+         struct cmsgcred cred;
+ #else
+         struct fcred cred;
+ #endif
+     } 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;
+
+     memset(&cmsg, 0, sizeof cmsg);
+     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\n", strerror(errno));
+         return STATUS_ERROR;
+     }
+     return STATUS_OK;
+ }
+ #endif
+
  static int
  pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
  {
***************
*** 442,447 ****
--- 500,509 ----

      switch (areq)
      {
+         case AUTH_REQ_PASSWORD:
+             /* discard const so we can assign it */
+             crypt_pwd = (char *)password;
+             break;
          case AUTH_REQ_CRYPT:
              crypt_pwd = crypt(password, conn->salt);
              break;
***************
*** 472,482 ****
                  break;
              }
          default:
!             /* discard const so we can assign it */
!             crypt_pwd = (char *)password;
!             break;
      }
-
      ret = pqPacketSend(conn, crypt_pwd, strlen(crypt_pwd) + 1);
      if (areq == AUTH_REQ_MD5)
          free(crypt_pwd);
--- 534,541 ----
                  break;
              }
          default:
!             return STATUS_ERROR;
      }
      ret = pqPacketSend(conn, crypt_pwd, strlen(crypt_pwd) + 1);
      if (areq == AUTH_REQ_MD5)
          free(crypt_pwd);
***************
*** 549,554 ****
--- 608,625 ----
                  return STATUS_ERROR;
              }
              break;
+
+         case AUTH_REQ_SCM_CREDS:
+ #ifdef SCM_CREDS
+             if (pg_local_sendauth(PQerrormsg, conn) != STATUS_OK)
+                 return STATUS_ERROR;
+ #else
+             snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+                      libpq_gettext("SCM_CRED authentication method not supported\n"));
+             return STATUS_ERROR;
+ #endif
+             break;
+
          default:
              snprintf(PQerrormsg, PQERRORMSG_LENGTH,
                       libpq_gettext("authentication method %u not supported\n"), areq);
Index: src/interfaces/odbc/connection.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/interfaces/odbc/connection.c,v
retrieving revision 1.33
diff -c -r1.33 connection.c
*** src/interfaces/odbc/connection.c    2001/08/15 18:42:16    1.33
--- src/interfaces/odbc/connection.c    2001/08/16 03:41:15
***************
*** 722,727 ****
--- 722,732 ----
                              self->errornumber = CONN_AUTH_TYPE_UNSUPPORTED;
                              return 0;

+                         case AUTH_REQ_SCM_CREDS:
+                             self->errormsg = "Unix socket credential authentication not supported";
+                             self->errornumber = CONN_AUTH_TYPE_UNSUPPORTED;
+                             return 0;
+
                          default:
                              self->errormsg = "Unknown authentication type";
                              self->errornumber = CONN_AUTH_TYPE_UNSUPPORTED;
Index: src/interfaces/odbc/connection.h
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/interfaces/odbc/connection.h,v
retrieving revision 1.25
diff -c -r1.25 connection.h
*** src/interfaces/odbc/connection.h    2001/08/15 18:42:16    1.25
--- src/interfaces/odbc/connection.h    2001/08/16 03:41:20
***************
*** 94,99 ****
--- 94,100 ----
  #define AUTH_REQ_PASSWORD                            3
  #define AUTH_REQ_CRYPT                                4
  #define AUTH_REQ_MD5                                5
+ #define AUTH_REQ_SCM_CREDS                            6

  /*    Startup Packet sizes */
  #define SM_DATABASE                                    64

pgsql-patches by date:

Previous
From: Bruce Momjian
Date:
Subject: Re: Re: [JDBC] JDBC pg_description update needed for CVS tip
Next
From: Peter Eisentraut
Date:
Subject: Re: encoding names