Thread: Add another AUTHTYPE for UNIX-domain connections

Add another AUTHTYPE for UNIX-domain connections

From
Anton Berezin
Date:
Hi,

On some platforms, it is possible to reliably determine the effective
credentials of a UNIX-domain peer.  Such functionality is not to be
missed from the PostgreSQL host-based authentication.  Its use allows
one to combine the convenience of the `trust' authmethod with a strict
checking of the connectee's identity.

I have chosen to re-use the existing pg_ident.conf mechanism for mapping
system user names to Postgres user names;  this should not present any
problems since `ident' authmethod is only applicable over TCP
connections, and the new `user' authmethod only works for local
connections;  one can always use different map names if one needs both
methods to co-exist (which is in my opinion very unlikely, considering
that the getpeereid() is a reliable method and that we all know how
reliable ident is).

The patch below implements this functionality, including necessary
changes to configure.in;  the only thing missing is documentation
patches - something I do not feel qualified to do, since English is not
my native language.

The patch is against 7.1.3 tarball - though I would not be surprised if
it applies fine against the current development version.  I verified
that it works as expected on FreeBSD 5.0-current.  I gather it should
work equally well on other platforms that have getpeereid(), which I
believe are at this moment OpenBSD-current and NetBSD-current.

Cheers,
%Anton.



diff -u -ruN --show-c-function /tmp/postgresql-7.1.3/configure ./configure
--- /tmp/postgresql-7.1.3/configure    Thu Aug 16 20:36:31 2001
+++ ./configure    Mon Dec  3 01:53:19 2001
@@ -5472,7 +5472,7 @@ EOF

 fi

-for ac_func in fcvt getopt_long memmove pstat setproctitle setsid sigprocmask sysconf waitpid dlopen fdatasync
+for ac_func in fcvt getopt_long memmove pstat setproctitle setsid sigprocmask sysconf waitpid dlopen fdatasync
getpeereid
 do
 echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
 echo "configure:5479: checking for $ac_func" >&5
diff -u -ruN --show-c-function /tmp/postgresql-7.1.3/configure.in ./configure.in
--- /tmp/postgresql-7.1.3/configure.in    Sun Dec  2 21:58:22 2001
+++ ./configure.in    Mon Dec  3 01:53:20 2001
@@ -759,7 +759,7 @@ PGAC_VAR_INT_TIMEZONE
 AC_FUNC_ACCEPT_ARGTYPES
 PGAC_FUNC_GETTIMEOFDAY_1ARG

-AC_CHECK_FUNCS([fcvt getopt_long memmove pstat setproctitle setsid sigprocmask sysconf waitpid dlopen fdatasync])
+AC_CHECK_FUNCS([fcvt getopt_long memmove pstat setproctitle setsid sigprocmask sysconf waitpid dlopen fdatasync
getpeereid])

 dnl Check whether <unistd.h> declares fdatasync().
 AC_EGREP_HEADER(fdatasync, unistd.h, AC_DEFINE(HAVE_FDATASYNC_DECL))
diff -u -ruN --show-c-function /tmp/postgresql-7.1.3/src/backend/libpq/auth.c ./src/backend/libpq/auth.c
--- /tmp/postgresql-7.1.3/src/backend/libpq/auth.c    Thu Mar 22 04:59:30 2001
+++ ./src/backend/libpq/auth.c    Mon Dec  3 01:53:20 2001
@@ -439,6 +439,9 @@ auth_failed(Port *port)
         case uaCrypt:
             authmethod = "Password";
             break;
+        case uaUser:
+            authmethod = "Peer eid";
+            break;
     }

     sprintf(buffer, "%s authentication failed for user '%s'",
@@ -536,6 +539,17 @@ be_recvauth(Port *port)

                 break;

+#ifdef HAVE_GETPEEREID
+            case uaUser:
+                if (authuser(port->sock, port->user, port->auth_arg) == STATUS_OK)
+                {
+                    areq = AUTH_REQ_OK;
+                    auth_handler = handle_done_auth;
+                }
+
+                break;
+#endif
+
             case uaPassword:
                 areq = AUTH_REQ_PASSWORD;
                 auth_handler = handle_password_auth;
@@ -762,6 +776,7 @@ map_old_to_new(Port *port, UserAuth old,
     {
             case uaCrypt:
             case uaReject:
+            case uaUser:
             status = STATUS_ERROR;
             break;

diff -u -ruN --show-c-function /tmp/postgresql-7.1.3/src/backend/libpq/hba.c ./src/backend/libpq/hba.c
--- /tmp/postgresql-7.1.3/src/backend/libpq/hba.c    Sat Feb 10 03:31:26 2001
+++ ./src/backend/libpq/hba.c    Mon Dec  3 01:53:20 2001
@@ -125,6 +125,10 @@ read_hba_entry2(FILE *file, UserAuth *us
         *userauth_p = uaReject;
     else if (strcmp(buf, "crypt") == 0)
         *userauth_p = uaCrypt;
+#ifdef HAVE_GETPEEREID
+    else if (strcmp(buf, "user") == 0)
+        *userauth_p = uaUser;
+#endif
     else
     {
         *error_p = true;
@@ -280,6 +284,11 @@ process_hba_record(FILE *file, hbaPort *

         read_hba_entry2(file, &port->auth_method, port->auth_arg, error_p);

+#ifdef HAVE_GETPEEREID
+        if (!*error_p && port->auth_method == uaUser)
+            *error_p = true;
+#endif
+
         if (*error_p)
             goto syntax;

@@ -781,8 +790,8 @@ verify_against_usermap(const char *pguse
         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
                "verify_against_usermap: hba configuration file does not "
            "have the usermap field filled in in the entry that pertains "
-          "to this connection.  That field is essential for Ident-based "
-                 "authentication.\n");
+            "to this connection.  That field is essential for Ident- or "
+                 "user-based authentication.\n");
         fputs(PQerrormsg, stderr);
         pqdebug("%s", PQerrormsg);
     }
@@ -867,6 +876,39 @@ authident(struct sockaddr_in * raddr, st
     return checks_out ? STATUS_OK : STATUS_ERROR;
 }

+int
+authuser(int sock, const char *postgres_username,
+         const char *auth_arg)
+{
+/*---------------------------------------------------------------------------
+  Get the effective credentials of a UNIX-domain peer.  Then lookup the
+  local UNIX user password entry.  Then look in the usermap file under
+  the usermap *auth_arg and see if that user is equivalent to
+  Postgres user *user.
+
+  Return STATUS_OK if yes.
+---------------------------------------------------------------------------*/
+#ifdef HAVE_GETPEEREID
+    bool        checks_out;
+    uid_t        euid;
+    gid_t        egid;
+    struct passwd *pw;
+
+    if (getpeereid(sock, &euid, &egid) != 0)
+        return STATUS_ERROR;
+
+    setpwent();
+    if ((pw = getpwuid(euid)) == NULL)
+        return STATUS_ERROR;
+
+    verify_against_usermap(postgres_username, pw->pw_name, auth_arg,
+                           &checks_out);
+
+    return checks_out ? STATUS_OK : STATUS_ERROR;
+#else
+    return STATUS_ERROR;
+#endif
+}

 #ifdef CYR_RECODE
 #define CHARSET_FILE "charset.conf"
diff -u -ruN --show-c-function /tmp/postgresql-7.1.3/src/backend/libpq/pg_hba.conf.sample
./src/backend/libpq/pg_hba.conf.sample
--- /tmp/postgresql-7.1.3/src/backend/libpq/pg_hba.conf.sample    Tue Nov 21 21:44:32 2000
+++ ./src/backend/libpq/pg_hba.conf.sample    Mon Dec  3 01:53:20 2001
@@ -121,10 +121,17 @@
 #
 #   krb5:       Kerberos V5 authentication is used.
 #
+#   user:     The system user identity is verified using getpeereid()
+#        system call.  This is only supported for local (UNIX
+#        socket) connections, and only on some platforms.  The
+#        correspondence between local system users and the
+#        requested PostgreSQL username is established using the
+#        same method as for "ident" AUTHTYPE.
+#
 #   reject:     Reject the connection.
 #
 # Local (UNIX socket) connections support only AUTHTYPEs "trust",
-# "password", "crypt", and "reject".
+# "password", "crypt", "user", and "reject".


 # Examples
@@ -146,6 +153,11 @@
 # host identifies him as (typically his Unix username):
 #
 # host       template1   192.168.93.0  255.255.255.0      ident     sameuser
+#
+# Allow any existing local unix user to connect to database "template1"
+# using the same PostgreSQL username:
+#
+# local      template1                                    user      sameuser
 #
 # Allow a user from host 192.168.12.10 to connect to database "template1"
 # if the user's password in pg_shadow is correctly supplied:
diff -u -ruN --show-c-function /tmp/postgresql-7.1.3/src/include/config.h.in ./src/include/config.h.in
--- /tmp/postgresql-7.1.3/src/include/config.h.in    Sun Apr 15 00:55:02 2001
+++ ./src/include/config.h.in    Mon Dec  3 01:53:20 2001
@@ -388,6 +388,9 @@
 /* Define if you have the setproctitle function.  */
 #undef HAVE_SETPROCTITLE

+/* Set to 1 if you have the getpeereid function. */
+#undef HAVE_GETPEEREID
+
 /* Define if you have the pstat function. */
 #undef HAVE_PSTAT

diff -u -ruN --show-c-function /tmp/postgresql-7.1.3/src/include/libpq/hba.h ./src/include/libpq/hba.h
--- /tmp/postgresql-7.1.3/src/include/libpq/hba.h    Thu Mar 22 05:00:47 2001
+++ ./src/include/libpq/hba.h    Mon Dec  3 01:53:20 2001
@@ -35,7 +35,8 @@ typedef enum UserAuth
     uaTrust,
     uaIdent,
     uaPassword,
-    uaCrypt
+    uaCrypt,
+    uaUser
 } UserAuth;

 typedef struct Port hbaPort;
@@ -43,5 +44,7 @@ typedef struct Port hbaPort;
 int            hba_getauthmethod(hbaPort *port);
 int authident(struct sockaddr_in * raddr, struct sockaddr_in * laddr,
           const char *postgres_username, const char *auth_arg);
+int authuser(int sock, const char *postgres_username,
+        const char *auth_arg);

 #endif



--
| Anton Berezin                |      FreeBSD: The power to serve |
| catpipe Systems ApS   _ _ |_ |           http://www.FreeBSD.org |
| tobez@catpipe.net    (_(_||  |                tobez@FreeBSD.org |
| +45 7021 0050                |         Private: tobez@tobez.org |

Re: Add another AUTHTYPE for UNIX-domain connections

From
Tom Lane
Date:
Anton Berezin <tobez@tobez.org> writes:
> On some platforms, it is possible to reliably determine the effective
> credentials of a UNIX-domain peer.

Umm ... I think this is in 7.2 already, although not using getpeereid.

Anyone have an idea whether there are platforms that have getpeereid but
not SO_PEERCRED or SCM_CREDS?  If so it might be worth adding getpeereid
as an alternate implementation in ident_unix().

            regards, tom lane

Re: Add another AUTHTYPE for UNIX-domain connections

From
Anton Berezin
Date:
On Sun, Dec 02, 2001 at 08:25:48PM -0500, Tom Lane wrote:
> Anton Berezin <tobez@tobez.org> writes:
> > On some platforms, it is possible to reliably determine the effective
> > credentials of a UNIX-domain peer.
>
> Umm ... I think this is in 7.2 already, although not using getpeereid.

Oops.  :-)
>
> Anyone have an idea whether there are platforms that have getpeereid but
> not SO_PEERCRED or SCM_CREDS?  If so it might be worth adding getpeereid
> as an alternate implementation in ident_unix().

Yes.  From FreeBSD's getpeereid(2) manpage:

IMPLEMENTATION NOTES
    On FreeBSD, getpeereid() is implemented in terms of the
    LOCAL_PEERCRED unix(4) socket option.

Cheers,
+Anton.
--
| Anton Berezin                |      FreeBSD: The power to serve |
| catpipe Systems ApS   _ _ |_ |           http://www.FreeBSD.org |
| tobez@catpipe.net    (_(_||  |                tobez@FreeBSD.org |
| +45 7021 0050                |         Private: tobez@tobez.org |

Re: Add another AUTHTYPE for UNIX-domain connections

From
Tom Lane
Date:
Anton Berezin <tobez@tobez.org> writes:
>> Anyone have an idea whether there are platforms that have getpeereid but
>> not SO_PEERCRED or SCM_CREDS?

> Yes.  From FreeBSD's getpeereid(2) manpage:

FreeBSD is known to be covered by the SCM_CREDS code we have now,
I believe.  Would you pull down 7.2beta3 and see if it works for you?

            regards, tom lane

Re: Add another AUTHTYPE for UNIX-domain connections

From
Bruce Momjian
Date:
> Anton Berezin <tobez@tobez.org> writes:
> > On some platforms, it is possible to reliably determine the effective
> > credentials of a UNIX-domain peer.
>
> Umm ... I think this is in 7.2 already, although not using getpeereid.
>
> Anyone have an idea whether there are platforms that have getpeereid but
> not SO_PEERCRED or SCM_CREDS?  If so it might be worth adding getpeereid
> as an alternate implementation in ident_unix().

OpenBSD implements only getpeereid().  I have added this to the TODO
list.   We already have the other BSD's covered, and Linux.  I am
concerned about moving to getpeereid() on the other BSD's because we
have working code already for them and I am not sure how new the OS has
to be to have getpeereid() support, i.e. is it in FreeBSD 4.4?  I don't
see a manual page for it, and FreeBSD 5.0 is not for major release until
another year so our current code seems best.

--
  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