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 200108191640.f7JGend24264@candle.pha.pa.us
Whole thread Raw
In response to Re: Patch: use SCM_CREDS authentication over PF_LOCAL sockets  (Tom Lane <tgl@sss.pgh.pa.us>)
List pgsql-patches
> Bruce Momjian <pgman@candle.pha.pa.us> writes:
> > Yes, but I mentioned PEERCRED is new in 7.2 and wasn't widely
> > distributed by Debian, so we should decide which we want first.  Also,
> > let me mention that this could turn out to be a portability headache.
>
> Based on your other message, SCM_CREDS will be much more of a
> portability headache than PEERCRED.  All the more reason to prefer
> the latter if available.

Agreed.  Patch attached.  Seems I had socket.h included twice too. Also,
is there a reason fe-auth has the PostgreSQL includes before the system
ones.

And also, there is #ifdef, but there is no version of #elif for #ifdef,
right?  Nothing like #eldef, so I have to use #elif defined().

--
  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.62
diff -c -r1.62 auth.c
*** src/backend/libpq/auth.c    2001/08/17 15:44:17    1.62
--- src/backend/libpq/auth.c    2001/08/19 16:37:30
***************
*** 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,521 ----
              break;

          case uaIdent:
+ #if !defined(SO_PEERCRED) && defined(SCM_CREDS)
+             /*
+              *    If we are doing ident on unix-domain sockets,
+              *    use SCM_CREDS only if it is defined and SO_PEERCRED isn't.
+              */
+ #ifdef fc_uid
+             /* Receive credentials on next message receipt, BSD/OS */
+             {
+                 int on = 1;
+                 if (setsockopt(port->sock, 0, LOCAL_CREDS, &on, sizeof(on)) < 0)
+                 {
+                     elog(FATAL,
+                          "pg_local_sendauth: can't do setsockopt: %s\n", strerror(errno));
+                     return;
+                 }
+             }
+ #endif
+             if (port->raddr.sa.sa_family ==    AF_UNIX)
+                 sendAuthRequest(port, AUTH_REQ_SCM_CREDS);
+ #endif
              status = authident(port);
              break;

***************
*** 676,678 ****
--- 699,702 ----

      return status;
  }
+
Index: src/backend/libpq/hba.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/hba.c,v
retrieving revision 1.64
diff -c -r1.64 hba.c
*** src/backend/libpq/hba.c    2001/08/16 16:24:15    1.64
--- src/backend/libpq/hba.c    2001/08/19 16:37:30
***************
*** 18,26 ****

  #include <errno.h>
  #include <pwd.h>
- #include <sys/types.h>
  #include <fcntl.h>
! #include <sys/socket.h>
  #include <netinet/in.h>
  #include <arpa/inet.h>
  #include <unistd.h>
--- 18,30 ----

  #include <errno.h>
  #include <pwd.h>
  #include <fcntl.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>
! #endif
  #include <netinet/in.h>
  #include <arpa/inet.h>
  #include <unistd.h>
***************
*** 876,914 ****
      {
          /* We didn't get a valid credentials struct. */
          snprintf(PQerrormsg, PQERRORMSG_LENGTH,
!                  "Could not get valid credentials from the UNIX socket: %s\n",
                   strerror(errno));
          fputs(PQerrormsg, stderr);
          pqdebug("%s", PQerrormsg);
          return false;
      }

-     /* Convert UID to user login name */
      pass = getpwuid(peercred.uid);

      if (pass == NULL)
      {
-         /* Error - no username with the given uid */
          snprintf(PQerrormsg, PQERRORMSG_LENGTH,
!                  "There is no entry in /etc/passwd with the socket's uid\n");
          fputs(PQerrormsg, stderr);
          pqdebug("%s", PQerrormsg);
          return false;
      }

!     StrNCpy(ident_user, pass->pw_name, IDENT_USERNAME_MAX);

      return true;

! #else /* not SO_PEERCRED */

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

! #endif /* SO_PEERCRED */
  }

  /*
--- 880,982 ----
      {
          /* We didn't get a valid credentials struct. */
          snprintf(PQerrormsg, PQERRORMSG_LENGTH,
!                  "ident_unix: error receiving credentials: %s\n",
                   strerror(errno));
          fputs(PQerrormsg, stderr);
          pqdebug("%s", PQerrormsg);
          return false;
      }

      pass = getpwuid(peercred.uid);

      if (pass == NULL)
      {
          snprintf(PQerrormsg, PQERRORMSG_LENGTH,
!              "ident_unix: unknown local user with uid %d\n",
          fputs(PQerrormsg, stderr);
          pqdebug("%s", PQerrormsg);
          return false;
      }

!     StrNCpy(ident_user, pass->pw_name, IDENT_USERNAME_MAX+1);

      return true;
+
+ #elif defined(SCM_CREDS)
+     struct msghdr msg;
+
+ /* Credentials structure */
+ #ifndef fc_uid
+     typedef struct cmsgcred Cred;
+ #define cruid cmcred_uid
+ #else
+     typedef struct fcred Cred;
+ #define cruid fc_uid
+ #endif
+     Cred *cred;
+
+     /* Compute size without padding */
+     char cmsgmem[sizeof(struct cmsghdr) + sizeof(Cred)];
+     /* Point to start of first structure */
+     struct cmsghdr *cmsg = (struct cmsghdr *)cmsgmem;
+
+     struct iovec iov;
+     char buf;
+     struct passwd *pw;
+
+     memset(&msg, 0, sizeof(msg));
+     msg.msg_iov = &iov;
+     msg.msg_iovlen = 1;
+     msg.msg_control = (char *)cmsg;
+     msg.msg_controllen = sizeof(cmsgmem);
+     memset(cmsg, 0, sizeof(cmsgmem));
+
+     /*
+      * 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 ||
+         cmsg->cmsg_len < sizeof(cmsgmem) ||
+         cmsg->cmsg_type != SCM_CREDS)
+     {
+         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+                  "ident_unix: error receiving credentials: %s\n",
+                  strerror(errno));
+         fputs(PQerrormsg, stderr);
+         pqdebug("%s", PQerrormsg);
+         return false;
+     }
+
+     cred = (Cred *)CMSG_DATA(cmsg);
+
+     pw = getpwuid(cred->fc_uid);
+     if (pw == NULL)
+     {
+         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+              "ident_unix: unknown local user with uid %d\n",
+              cred->fc_uid);
+         fputs(PQerrormsg, stderr);
+         pqdebug("%s", PQerrormsg);
+         return false;
+     }
+
+     StrNCpy(ident_user, pw->pw_name, IDENT_USERNAME_MAX+1);

!     return true;

+ #else
      snprintf(PQerrormsg, PQERRORMSG_LENGTH,
               "IDENT auth is not supported on local connections on this platform\n");
      fputs(PQerrormsg, stderr);
      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.25
diff -c -r1.25 pg_hba.conf.sample
*** src/backend/libpq/pg_hba.conf.sample    2001/08/16 16:24:16    1.25
--- src/backend/libpq/pg_hba.conf.sample    2001/08/19 16:37:33
***************
*** 127,138 ****
  #   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
--- 127,138 ----
  #   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.57
diff -c -r1.57 pqcomm.h
*** src/include/libpq/pqcomm.h    2001/08/16 04:27:18    1.57
--- src/include/libpq/pqcomm.h    2001/08/19 16:37:34
***************
*** 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.55
diff -c -r1.55 fe-auth.c
*** src/interfaces/libpq/fe-auth.c    2001/08/17 15:40:07    1.55
--- src/interfaces/libpq/fe-auth.c    2001/08/19 16:37:35
***************
*** 30,35 ****
--- 30,36 ----

  #include "postgres_fe.h"

+ /* XXX is there a reason these appear before the system defines? */
  #include "libpq-fe.h"
  #include "libpq-int.h"
  #include "fe-auth.h"
***************
*** 40,45 ****
--- 41,53 ----
  #else
  #include <unistd.h>
  #include <fcntl.h>
+ #include <errno.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>
+ #endif
  #include <sys/param.h>            /* for MAXHOSTNAMELEN on most */
  #ifndef  MAXHOSTNAMELEN
  #include <netdb.h>                /* for MAXHOSTNAMELEN on some */
***************
*** 428,433 ****
--- 436,488 ----

  #endif     /* KRB5 */

+ #ifdef SCM_CREDS
+ static int
+ pg_local_sendauth(char *PQerrormsg, PGconn *conn)
+ {
+     char buf;
+     struct iovec iov;
+     struct msghdr msg;
+ #ifndef fc_uid
+     /* Prevent padding */
+     char cmsgmem[sizeof(struct cmsghdr) + sizeof(struct cmsgcred)];
+     /* Point to start of first structure */
+     struct cmsghdr *cmsg = (struct cmsghdr *)cmsgmem;
+ #endif
+
+     /*
+      * 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(&msg, 0, sizeof(msg));
+     msg.msg_iov = &iov;
+     msg.msg_iovlen = 1;
+
+ #ifndef fc_uid
+     /* Create control header, FreeBSD */
+     msg.msg_control = cmsg;
+     msg.msg_controllen = sizeof(cmsgmem);
+     memset(cmsg, 0, sizeof(cmsgmem));
+     cmsg.hdr.cmsg_len = sizeof(cmsgmem);
+     cmsg.hdr.cmsg_level = SOL_SOCKET;
+     cmsg.hdr.cmsg_type = SCM_CREDS;
+ #endif
+
+     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)
  {
***************
*** 473,484 ****
              crypt_pwd = crypt(password, salt);
              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);
--- 528,540 ----
              crypt_pwd = crypt(password, salt);
              break;
          }
!         case AUTH_REQ_PASSWORD:
              /* discard const so we can assign it */
              crypt_pwd = (char *)password;
              break;
+         default:
+             return STATUS_ERROR;
      }
      ret = pqPacketSend(conn, crypt_pwd, strlen(crypt_pwd) + 1);
      if (areq == AUTH_REQ_MD5)
          free(crypt_pwd);
***************
*** 551,556 ****
--- 607,624 ----
                  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.34
diff -c -r1.34 connection.c
*** src/interfaces/odbc/connection.c    2001/08/17 02:59:20    1.34
--- src/interfaces/odbc/connection.c    2001/08/19 16:37:36
***************
*** 724,729 ****
--- 724,734 ----
                              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/19 16:37:36
***************
*** 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: Peter Eisentraut
Date:
Subject: LIKE indexing
Next
From: Tom Lane
Date:
Subject: Re: LIKE indexing