Kerberos v5 support - Mailing list pgsql-patches

From Garrett Wollman
Subject Kerberos v5 support
Date
Msg-id 200011061656.LAA73531@khavrinen.lcs.mit.edu
Whole thread Raw
Responses Re: Kerberos v5 support  (Bruce Momjian <pgman@candle.pha.pa.us>)
Re: Kerberos v5 support  (Peter Eisentraut <peter_e@gmx.net>)
List pgsql-patches
I thought I sent this back in September, but I can't find anything in
the mailing-list archives, so I am assuming it fell into a black hole.

Enclosed please find a set of patches, relative to 7.0.2, which will
result in Kerberos v5 support which both compiles and works (as in,
I've successfully authenticated as a remote client).

Our pg_hba.conf file then looks like:

local        all                                           trust
host         all         0.0.0.0        0.0.0.0         krb5

However, that `trust' is tempered by changes to the startup scripts
(not included here) which force the local-domain socket to mode 600,
thereby ensuring that all normal clients connect via an authenticated
network connection.

You can see from some of the comments that I'd like this to be made
stronger in a number of ways.  This patch set simply gets pgsql up to
the minimum acceptable level of security for our environment and
application.

My original message follows.

------------------------------------------------------------------------

The enclosed patches fix the Kerberos v5 support in libpq and the
backend.  It was totally broken before, now it's still broken but
works.  (That is to say: before, it didn't work, and now it does work
but doesn't provide the level of security it should.  Still better
than plaintext passwords.)  So far as I can tell, the original code
was written for an early beta version, and rotted severely in the
intervening years.  We actually need authentication to work, which is
why I'm doing this now.

A few notes:

- See the comment near pg_an_to_ln() about one part of the brokenness.

- As implemented, this code will not work over PF_LOCAL sockets.

- Some things don't work correctly in the absence of a `local all
trust' line in pg_hba.conf, and PGUSER needs to be set in order for
*that* to work.

- E2E encryption would really be preferable.  It looks fairly easy to
do in the fe->be side of the protocol, but it's not obviously possible
for the other direction.  In any event, I wanted to confine my changes
to the smallest number of source files, so I didn't make any effort to
implement this.  Either way, a protocol change is required.

-GAWollman

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#    patch-be
#    patch-bf
#    patch-bg
#    patch-bh
#
echo x - patch-be
sed 's/^X//' >patch-be << 'END-of-patch-be'
X--- backend/libpq/auth.c.orig    Wed Apr 12 13:15:13 2000
X+++ backend/libpq/auth.c    Wed Sep 27 23:33:17 2000
X@@ -142,7 +142,4 @@
X
X #ifdef KRB5
X-/* This needs to be ifdef'd out because krb5.h doesn't exist.  This needs
X-   to be fixed.
X-*/
X /*----------------------------------------------------------------
X  * MIT Kerberos authentication system - protocol version 5
X@@ -150,5 +147,15 @@
X  */
X
X-#include "krb5/krb5.h"
X+#include "krb5.h"
X+#ifndef PG_KRB_SRVNAM
X+#define    PG_KRB_SRVNAM    "pgsql"
X+#endif
X+#ifndef PG_KRB_KEYTAB
X+#define PG_KRB_KEYTAB    "FILE:/etc/keytab.pgsql"
X+#endif
X+
X+static krb5_context    mycontext;
X+static int        mycontext_inited;
X+static krb5_keytab    keytab;
X
X /*
X@@ -156,12 +163,11 @@
X  *                  name
X  *
X- * XXX Assumes that the first aname component is the user name.  This is NOT
X- *       necessarily so, since an aname can actually be something out of your
X- *       worst X.400 nightmare, like
X- *          ORGANIZATION=U. C. Berkeley/NAME=Paul M. Aoki@CS.BERKELEY.EDU
X- *       Note that the MIT an_to_ln code does the same thing if you don't
X- *       provide an aname mapping database...it may be a better idea to use
X- *       krb5_an_to_ln, except that it punts if multiple components are found,
X- *       and we can't afford to punt.
X+ * XXX - this is totally broken (and potentially insecure on the server side).
X+ * The correct mechanism is to use the entire principal name, and make
X+ * the server do a table lookup to discover the mapping.
X+ * (In the protocol as it stands, if user jrl@A.EXAMPLE.COM authenticates to
X+ * a server in the B.EXAMPLE.COM realm, the server will accept him as
X+ * local-user `jrl' regardless of whether or not jrl@A.EXAMPLE.COM is
X+ * the same user as jrl@B.EXAMPLE.COM.)
X  */
X static char *
X@@ -200,68 +206,117 @@
X pg_krb5_recvauth(Port *port)
X {
X-    char        servbuf[MAXHOSTNAMELEN + 1 +
X-                                    sizeof(PG_KRB_SRVNAM)];
X-    char       *hostp,
X-               *kusername = (char *) NULL;
X+    char        hostbuf[MAXHOSTNAMELEN + 1];
X+    char        *kusername = (char *) NULL;
X     krb5_error_code code;
X-    krb5_principal client,
X-                server;
X-    krb5_address sender_addr;
X-    krb5_rdreq_key_proc keyproc = (krb5_rdreq_key_proc) NULL;
X-    krb5_pointer keyprocarg = (krb5_pointer) NULL;
X+    krb5_principal    server;
X+    krb5_auth_context authctx;
X+    krb5_authenticator *them;
X+
X+    if (!mycontext_inited)
X+    {
X+        code = krb5_init_context(&mycontext);
X+        if (code)
X+        {
X+            snprintf(PQerrormsg, PQERRORMSG_LENGTH,
X+                 "pg_krb5_recvauth: krb5_init_context: %s\n",
X+                 error_message(code));
X+            fputs(PQerrormsg, stderr);
X+            pqdebug("%s", PQerrormsg);
X+            return STATUS_ERROR;
X+        }
X+        if (strcmp(PG_KRB_KEYTAB, "default") == 0)
X+        {
X+            code = krb5_kt_default(mycontext, &keytab);
X+        }
X+        else
X+        {
X+            code = krb5_kt_resolve(mycontext, PG_KRB_KEYTAB,
X+                           &keytab);
X+        }
X+        if (code)
X+        {
X+            snprintf(PQerrormsg, PQERRORMSG_LENGTH,
X+                 "pg_krb5_recvauth: keytab %s: %s\n",
X+                 PG_KRB_KEYTAB,
X+                 error_message(code));
X+            fputs(PQerrormsg, stderr);
X+            pqdebug("%s", PQerrormsg);
X+            return STATUS_ERROR;
X+        }
X+        mycontext_inited = 1;
X+    }
X
X-    /*
X-     * Set up server side -- since we have no ticket file to make this
X-     * easy, we construct our own name and parse it.  See note on
X-     * canonicalization above.
X-     */
X-    strcpy(servbuf, PG_KRB_SRVNAM);
X-    *(hostp = servbuf + (sizeof(PG_KRB_SRVNAM) - 1)) = '/';
X-    if (gethostname(++hostp, MAXHOSTNAMELEN) < 0)
X-        strcpy(hostp, "localhost");
X-    if (hostp = strchr(hostp, '.'))
X-        *hostp = '\0';
X-    if (code = krb5_parse_name(servbuf, &server))
X+    code = krb5_auth_con_init(mycontext, &authctx);
X+    if (code)
X+    {
X+        snprintf(PQerrormsg, PQERRORMSG_LENGTH,
X+             "pg_krb5_recvauth: krb5_auth_con_init: %s\n",
X+             error_message(code));
X+        fputs(PQerrormsg, stderr);
X+        pqdebug("%s", PQerrormsg);
X+        return STATUS_ERROR;
X+    }
X+
X+    if (gethostname(hostbuf, MAXHOSTNAMELEN) < 0)
X     {
X         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
X-        "pg_krb5_recvauth: Kerberos error %d in krb5_parse_name\n", code);
X-        com_err("pg_krb5_recvauth", code, "in krb5_parse_name");
X+             "pg_krb5_recvauth: gethostname: %s\n",
X+             strerror(errno));
X+        fputs(PQerrormsg, stderr);
X+        pqdebug("%s", PQerrormsg);
X+        krb5_auth_con_free(mycontext, authctx);
X         return STATUS_ERROR;
X     }
X
X+    code = krb5_sname_to_principal(mycontext, hostbuf, PG_KRB_SRVNAM,
X+                       KRB5_NT_SRV_HST, &server);
X+    if (code)
X+    {
X+        snprintf(PQerrormsg, PQERRORMSG_LENGTH,
X+             "pg_krb5_recvauth: krb5_sname_to_principal: %s\n",
X+             error_message(code));
X+        fputs(PQerrormsg, stderr);
X+        pqdebug("%s", PQerrormsg);
X+        krb5_auth_con_free(mycontext, authctx);
X+        return STATUS_ERROR;
X+    }
X+
X     /*
X      * krb5_sendauth needs this to verify the address in the client
X      * authenticator.
X      */
X-    sender_addr.addrtype = port->raddr.in.sin_family;
X-    sender_addr.length = sizeof(port->raddr.in.sin_addr);
X-    sender_addr.contents = (krb5_octet *) & (port->raddr.in.sin_addr);
X-
X-    if (strcmp(PG_KRB_SRVTAB, ""))
X+#define    ALL    (KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR | \
X+         KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR)
X+    code = krb5_auth_con_genaddrs(mycontext, authctx, port->sock, ALL);
X+#undef ALL
X+
X+    code = krb5_recvauth(mycontext, &authctx, (krb5_pointer)&port->sock,
X+                 PG_KRB5_VERSION, server, 0, keytab,
X+                 (krb5_ticket **)0);
X+    if (code)
X     {
X-        keyproc = krb5_kt_read_service_key;
X-        keyprocarg = PG_KRB_SRVTAB;
X+        snprintf(PQerrormsg, PQERRORMSG_LENGTH,
X+             "pg_krb5_recvauth: krb5_recvauth: %s\n",
X+             error_message(code));
X+        fputs(PQerrormsg, stderr);
X+        pqdebug("%s", PQerrormsg);
X+        krb5_free_principal(mycontext, server);
X+        krb5_auth_con_free(mycontext, authctx);
X+        return STATUS_ERROR;
X     }
X+    krb5_free_principal(mycontext, server);
X
X-    if (code = krb5_recvauth((krb5_pointer) & port->sock,
X-                             PG_KRB5_VERSION,
X-                             server,
X-                             &sender_addr,
X-                             (krb5_pointer) NULL,
X-                             keyproc,
X-                             keyprocarg,
X-                             (char *) NULL,
X-                             (krb5_int32 *) NULL,
X-                             &client,
X-                             (krb5_ticket **) NULL,
X-                             (krb5_authenticator **) NULL))
X+    code = krb5_auth_con_getauthenticator(mycontext, authctx, &them);
X+    if (code)
X     {
X         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
X-         "pg_krb5_recvauth: Kerberos error %d in krb5_recvauth\n", code);
X-        com_err("pg_krb5_recvauth", code, "in krb5_recvauth");
X-        krb5_free_principal(server);
X+         "pg_krb5_recvauth: getauthenticator: %s\n",
X+             error_message(code));
X+        fputs(PQerrormsg, stderr);
X+        pqdebug("%s", PQerrormsg);
X+        krb5_free_principal(mycontext, server);
X+        krb5_auth_con_free(mycontext, authctx);
X         return STATUS_ERROR;
X     }
X-    krb5_free_principal(server);
X
X     /*
X@@ -270,13 +325,18 @@
X      * postmaster startup packet.
X      */
X-    if ((code = krb5_unparse_name(client, &kusername)))
X+    if ((code = krb5_unparse_name(mycontext, them->client, &kusername)))
X     {
X         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
X-                 "pg_krb5_recvauth: Kerberos error %d in krb5_unparse_name\n", code);
X-        com_err("pg_krb5_recvauth", code, "in krb5_unparse_name");
X-        krb5_free_principal(client);
X+             "pg_krb5_recvauth: krb5_unparse_name: %s\n",
X+             error_message(code));
X+        fputs(PQerrormsg, stderr);
X+        pqdebug("%s", PQerrormsg);
X+        krb5_free_authenticator(mycontext, them);
X+        krb5_auth_con_free(mycontext, authctx);
X         return STATUS_ERROR;
X     }
X-    krb5_free_principal(client);
X+    krb5_free_authenticator(mycontext, them);
X+    krb5_auth_con_free(mycontext, authctx);
X+
X     if (!kusername)
X     {
X@@ -288,5 +348,5 @@
X     }
X     kusername = pg_an_to_ln(kusername);
X-    if (strncmp(username, kusername, SM_USER))
X+    if (strncmp(port->user, kusername, SM_USER))
X     {
X         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
X@@ -294,8 +354,8 @@
X         fputs(PQerrormsg, stderr);
X         pqdebug("%s", PQerrormsg);
X-        pfree(kusername);
X+        free(kusername);
X         return STATUS_ERROR;
X     }
X-    pfree(kusername);
X+    free(kusername);
X     return STATUS_OK;
X }
END-of-patch-be
echo x - patch-bf
sed 's/^X//' >patch-bf << 'END-of-patch-bf'
Xdiff -ru2 old/configure.in configure.in
X--- old/configure.in    Wed May 24 18:43:59 2000
X+++ configure.in    Tue Sep 26 22:39:46 2000
X@@ -369,4 +369,55 @@
X export USE_ODBC
X
X+AC_ARG_WITH(
X+    krb5,
X+    [  --with-krb5[=PREFIX]    build Kerberos 5 authentication support ],
X+    [
X+    case "$withval" in
X+    y | ye | yes)    USE_KRB5=true;;
X+    n | no)    USE_KRB5=false;;
X+    *)    USE_KRB5=true
X+        KRB5_INCS="-I$withval/include $KRB5_INCS"
X+        KRB5_LIBS="-L$withval/lib $KRB5_LIBS";;
X+    esac
X+    ],
X+    [ USE_KRB5=false ]
X+)
X+
X+AC_ARG_WITH(
X+    krb-service-name,
X+    [  --with-krb-service-name=NAME authenticate as Kerberos principal NAME ],
X+    [  KRB_SRVNAM="$withval" ],
X+    [  unset KRB_SRVNAM ]
X+)
X+
X+AC_ARG_WITH(
X+    krb-keytab,
X+    [  --with-krb-keytab=FILE:FILENAME use Kerberos v5 keytab FILENAME ],
X+    [  KRB_KEYTAB="$withval" ],
X+    [  unset KRB_KEYTAB ]
X+)
X+
X+export USE_KRB5
X+export KRB5_INCS
X+export KRB5_LIBS
X+
X+AC_ARG_WITH(
X+    openssl,
X+    [  --with-openssl[=PREFIX] build OpenSSL transport support ],
X+    [
X+    case "$withval" in
X+    y | ye | yes)    USE_OPENSSL=true;;
X+    n | no)    USE_OPENSSL=false;;
X+    *)    USE_OPENSSL=true
X+        OPENSSL_INCS="-I$withval $OPENSSL_INCS"
X+        OPENSSL_LIBS="-L$withval $OPENSSL_LIBS -lopenssl -lcrypto";;
X+    esac
X+    ],
X+    [ USE_OPENSSL=false ]
X+)
X+export USE_OPENSSL
X+export OPENSSL_INCS
X+export OPENSSL_LIBS
X+
X AC_MSG_CHECKING(setproctitle)
X AC_ARG_WITH(
X@@ -509,4 +560,6 @@
X AC_SUBST(USE_ODBC)
X AC_SUBST(MULTIBYTE)
X+AC_SUBST(USE_KRB5)
X+AC_SUBST(USE_OPENSSL)
X
X dnl Check for C++ support (allow override if needed)
X@@ -1312,4 +1365,50 @@
X     CPPFLAGS="$ice_save_CPPFLAGS"
X     LDFLAGS="$ice_save_LDFLAGS"
X+fi
X+
X+dnl
X+dnl User requested Kerberos support in libpq; see if the required
X+dnl header files and libraries are actually there.
X+dnl
X+if $USE_KRB5; then
X+    AC_CHECKING(if Kerberos 5 environment is complete)
X+    OCFLAGS="$CFLAGS"
X+    OCPPFLAGS="$CPPFLAGS"
X+    OLIBS="$LIBS"
X+    if test "$KRB5_INCS"; then
X+        CFLAGS="$CFLAGS $KRB5_INCS"
X+        CPPFLAGS="$CPPFLAGS $KRB5_INCS"
X+    fi
X+    if test "$KRB5_LIBS"; then
X+        LIBS="$LIBS $KRB5_LIBS"
X+    fi
X+    AC_CHECK_HEADERS(krb5.h)
X+    AC_CHECK_LIB(krb5, krb5_recvauth, , , [-lk5crypto -lcom_err])
X+    if test "$ac_cv_header_krb5_h" = "no"; then
X+        AC_MSG_WARN([krb5.h not found; disabling Kerberos v5 support])
X+        USE_KRB5=false
X+        CFLAGS="$OCFLAGS"
X+        LIBS="$OLIBS"
X+    else
X+        if test "$ac_cv_lib_krb5" = "no"; then
X+            AC_MSG_WARN([libkrb5 not found; disabling Kerberos v5 support])
X+            USE_KRB5=false
X+        else
X+        fi
X+    fi
X+    if $USE_KRB5; then
X+        CFLAGS="$CFLAGS -DKRB5 -DUSE_KRB5"
X+        LIBS="$LIBS -lkrb5 -lk5crypto -lcom_err"
X+        if test "$KRB_SRVNAM"; then
X+            CFLAGS="$CFLAGS -DPG_KRB_SRVNAM=\\\"$KRB_SRVNAM\\\""
X+        fi
X+        if test "$KRB_KEYTAB"; then
X+            CFLAGS="$CFLAGS -DPG_KRB_KEYTAB=\\\"$KRB_KEYTAB\\\""
X+        fi
X+    else
X+        CFLAGS="$OCFLAGS"
X+        CPPFLAGS="$OCPPFLAGS"
X+        LIBS="$OLIBS"
X+    fi
X fi
X
END-of-patch-bf
echo x - patch-bg
sed 's/^X//' >patch-bg << 'END-of-patch-bg'
Xdiff -ru2 old/interfaces/libpq/Makefile.in interfaces/libpq/Makefile.in
X--- old/interfaces/libpq/Makefile.in    Thu Apr 13 20:42:06 2000
X+++ interfaces/libpq/Makefile.in    Tue Sep 26 23:50:45 2000
X@@ -34,4 +34,8 @@
X # make sure it gets included in shared libpq.
X SHLIB_LINK+= $(findstring -lcrypt,$(LIBS))
X+SHLIB_LINK+= $(filter -L%,$(LIBS))
X+SHLIB_LINK+= $(findstring -lkrb5,$(LIBS))
X+SHLIB_LINK+= $(findstring -lk5crypto,$(LIBS))
X+SHLIB_LINK+= $(findstring -lcom_err,$(LIBS))
X
X # Shared library stuff, also default 'all' target
END-of-patch-bg
echo x - patch-bh
sed 's/^X//' >patch-bh << 'END-of-patch-bh'
X--- interfaces/libpq/fe-auth.c.orig    Wed Apr 12 13:17:13 2000
X+++ interfaces/libpq/fe-auth.c    Wed Sep 27 23:15:30 2000
X@@ -235,5 +235,9 @@
X  */
X
X-#include "krb5/krb5.h"
X+#include <fcntl.h>
X+#include "krb5.h"
X+#ifndef PG_KRB_SRVNAM
X+#define    PG_KRB_SRVNAM    "pgsql"
X+#endif
X
X /*
X@@ -241,12 +245,11 @@
X  *                  name
X  *
X- * XXX Assumes that the first aname component is the user name.  This is NOT
X- *       necessarily so, since an aname can actually be something out of your
X- *       worst X.400 nightmare, like
X- *          ORGANIZATION=U. C. Berkeley/NAME=Paul M. Aoki@CS.BERKELEY.EDU
X- *       Note that the MIT an_to_ln code does the same thing if you don't
X- *       provide an aname mapping database...it may be a better idea to use
X- *       krb5_an_to_ln, except that it punts if multiple components are found,
X- *       and we can't afford to punt.
X+ * XXX - this is totally broken (and potentially insecure on the server side).
X+ * The correct mechanism is to use the entire principal name, and make
X+ * the server do a table lookup to discover the mapping.
X+ * (In the protocol as it stands, if user jrl@A.EXAMPLE.COM authenticates to
X+ * a server in the B.EXAMPLE.COM realm, the server will accept him as
X+ * local-user `jrl' regardless of whether or not jrl@A.EXAMPLE.COM is
X+ * the same user as jrl@B.EXAMPLE.COM.)
X  */
X static char *
X@@ -260,63 +263,37 @@
X }
X
X+static krb5_context mycontext;
X+static int mycontext_inited;
X+static krb5_ccache ccache;
X
X-/*
X- * pg_krb5_init -- initialization performed before any Kerberos calls are made
X- *
X- * With v5, we can no longer set the ticket (credential cache) file name;
X- * we now have to provide a file handle for the open (well, "resolved")
X- * ticket file everywhere.
X- *
X- */
X-static int
X-            krb5_ccache
X+static krb5_error_code
X pg_krb5_init(void)
X {
X     krb5_error_code code;
X-    char       *realm,
X-               *defname;
X-    char        tktbuf[MAXPGPATH];
X-    static krb5_ccache ccache = (krb5_ccache) NULL;
X
X-    if (ccache)
X-        return ccache;
X-
X-    /*
X-     * If the user set PGREALM, then we use a ticket file with a special
X-     * name: <usual-ticket-file-name>@<PGREALM-value>
X-     */
X-    if (!(defname = krb5_cc_default_name()))
X-    {
X-        (void) sprintf(PQerrormsg,
X-                       "pg_krb5_init: krb5_cc_default_name failed\n");
X-        return (krb5_ccache) NULL;
X-    }
X-    strcpy(tktbuf, defname);
X-    if (realm = getenv("PGREALM"))
X-    {
X-        strcat(tktbuf, "@");
X-        strcat(tktbuf, realm);
X-    }
X+    if (mycontext_inited)
X+        return 0;
X
X-    if (code = krb5_cc_resolve(tktbuf, &ccache))
X-    {
X-        (void) sprintf(PQerrormsg,
X-           "pg_krb5_init: Kerberos error %d in krb5_cc_resolve\n", code);
X-        com_err("pg_krb5_init", code, "in krb5_cc_resolve");
X-        return (krb5_ccache) NULL;
X-    }
X-    return ccache;
X+    code = krb5_init_context(&mycontext);
X+    if (code)
X+        return code;
X+
X+    code = krb5_cc_default(mycontext, &ccache);
X+    if (code)
X+        return code;
X+    mycontext_inited = 1;
X+    return 0;
X }
X
X /*
X  * pg_krb5_authname -- returns a pointer to static space containing whatever
X- *                       name the user has authenticated to the system
X+ *                name the user has authenticated to the system
X  *
X  * We obtain this information by digging around in the ticket file.
X+ * XXX see comments above
X  */
X-static const char *
X-pg_krb5_authname(const char *PQerrormsg)
X+static char *
X+pg_krb5_authname(char *PQerrormsg)
X {
X-    krb5_ccache ccache;
X     krb5_principal principal;
X     krb5_error_code code;
X@@ -326,22 +303,33 @@
X         return authname;
X
X-    ccache = pg_krb5_init();    /* don't free this */
X+    code = pg_krb5_init();
X+    if (code)
X+    {
X+        sprintf(PQerrormsg, "pg_krb5_init: %s\n",
X+            error_message(code));
X+        fputs(PQerrormsg, stderr);
X+        return (char *)NULL;
X+    }
X
X-    if (code = krb5_cc_get_principal(ccache, &principal))
X+    code = krb5_cc_get_principal(mycontext, ccache, &principal);
X+    if (code)
X     {
X         (void) sprintf(PQerrormsg,
X-                       "pg_krb5_authname: Kerberos error %d in krb5_cc_get_principal\n", code);
X-        com_err("pg_krb5_authname", code, "in krb5_cc_get_principal");
X+                   "pg_krb5_authname: krb5_cc_get_principal: %s\n",
X+                   error_message(code));
X+        fputs(PQerrormsg, stderr);
X         return (char *) NULL;
X     }
X-    if (code = krb5_unparse_name(principal, &authname))
X+    code = krb5_unparse_name(mycontext, principal, &authname);
X+    if (code)
X     {
X         (void) sprintf(PQerrormsg,
X-                       "pg_krb5_authname: Kerberos error %d in krb5_unparse_name\n", code);
X-        com_err("pg_krb5_authname", code, "in krb5_unparse_name");
X-        krb5_free_principal(principal);
X+                   "pg_krb5_authname: krb5_unparse_name: %s\n",
X+                   error_message(code));
X+        fputs(PQerrormsg, stderr);
X+        krb5_free_principal(mycontext, principal);
X         return (char *) NULL;
X     }
X-    krb5_free_principal(principal);
X+    krb5_free_principal(mycontext, principal);
X     return pg_an_to_ln(authname);
X }
X@@ -349,107 +337,125 @@
X /*
X  * pg_krb5_sendauth -- client routine to send authentication information to
X- *                       the server
X- *
X- * This routine does not do mutual authentication, nor does it return enough
X- * information to do encrypted connections.  But then, if we want to do
X- * encrypted connections, we'll have to redesign the whole RPC mechanism
X- * anyway.
X+ *               the server
X  *
X- * Server hostnames are canonicalized v4-style, i.e., all domain suffixes
X- * are simply chopped off.    Hence, we are assuming that you've entered your
X- * server instances as
X- *        <value-of-PG_KRB_SRVNAM>/<canonicalized-hostname>
X- * in the PGREALM (or local) database.    This is probably a bad assumption.
X  */
X static int
X-pg_krb5_sendauth(const char *PQerrormsg, int sock,
X-                 struct sockaddr_in * laddr,
X-                 struct sockaddr_in * raddr,
X-                 const char *hostname)
X+pg_krb5_sendauth(char *PQerrormsg, int sock,
X+         struct sockaddr_in * laddr,
X+         struct sockaddr_in * raddr,
X+         const char *hostname)
X {
X-    char        servbuf[MAXHOSTNAMELEN + 1 +
X-                                    sizeof(PG_KRB_SRVNAM)];
X-    const char *hostp;
X-    const char *realm;
X+    int sflags;
X+    char servbuf[MAXHOSTNAMELEN + 1];
X     krb5_error_code code;
X-    krb5_principal client,
X-                server;
X-    krb5_ccache ccache;
X+    krb5_principal server;
X     krb5_error *error = (krb5_error *) NULL;
X+    krb5_auth_context authctx;
X
X-    ccache = pg_krb5_init();    /* don't free this */
X+    code = pg_krb5_init();
X+    if (code)
X+    {
X+        sprintf(PQerrormsg, "pg_krb5_init: %s\n",
X+            error_message(code));
X+        fputs(PQerrormsg, stderr);
X+        return STATUS_ERROR;
X+    }
X
X-    /*
X-     * set up client -- this is easy, we can get it out of the ticket
X-     * file.
X-     */
X-    if (code = krb5_cc_get_principal(ccache, &client))
X+    code = krb5_auth_con_init(mycontext, &authctx);
X+    if (code)
X     {
X-        (void) sprintf(PQerrormsg,
X-                       "pg_krb5_sendauth: Kerberos error %d in krb5_cc_get_principal\n", code);
X-        com_err("pg_krb5_sendauth", code, "in krb5_cc_get_principal");
X+        sprintf(PQerrormsg,
X+            "pg_krb5_recvauth: krb5_auth_con_init: %s\n",
X+            error_message(code));
X+        fputs(PQerrormsg, stderr);
X         return STATUS_ERROR;
X     }
X
X-    /*
X-     * set up server -- canonicalize as described above
X-     */
X-    strcpy(servbuf, PG_KRB_SRVNAM);
X-    *(hostp = servbuf + (sizeof(PG_KRB_SRVNAM) - 1)) = '/';
X-    if (hostname || *hostname)
X-        strncpy(++hostp, hostname, MAXHOSTNAMELEN);
X-    else
X-    {
X-        if (gethostname(++hostp, MAXHOSTNAMELEN) < 0)
X-            strcpy(hostp, "localhost");
X-    }
X-    if (hostp = strchr(hostp, '.'))
X-        *hostp = '\0';
X-    if (realm = getenv("PGREALM"))
X+    if (hostname == 0 || *hostname == '\0')
X     {
X-        strcat(servbuf, "@");
X-        strcat(servbuf, realm);
X+        if (gethostname(servbuf, MAXHOSTNAMELEN) < 0)
X+        {
X+            sprintf(PQerrormsg,
X+                 "pg_krb5_sendauth: gethostname: %s\n",
X+                 strerror(errno));
X+            fputs(PQerrormsg, stderr);
X+            krb5_auth_con_free(mycontext, authctx);
X+            return STATUS_ERROR;
X+        }
X+        hostname = servbuf;
X     }
X-    if (code = krb5_parse_name(servbuf, &server))
X+
X+    code = krb5_sname_to_principal(mycontext, hostname, PG_KRB_SRVNAM,
X+                       KRB5_NT_SRV_HST, &server);
X+
X+    if (code)
X     {
X-        (void) sprintf(PQerrormsg,
X-        "pg_krb5_sendauth: Kerberos error %d in krb5_parse_name\n", code);
X-        com_err("pg_krb5_sendauth", code, "in krb5_parse_name");
X-        krb5_free_principal(client);
X+        sprintf(PQerrormsg,
X+            "pg_krb5_sendauth: krb5_sname_to_principal: %s\n",
X+            error_message(code));
X+        fputs(PQerrormsg, stderr);
X+        krb5_auth_con_free(mycontext, authctx);
X+        return STATUS_ERROR;
X+    }
X+
X+#define    ALL    (KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR | \
X+         KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR)
X+    code = krb5_auth_con_genaddrs(mycontext, authctx, sock, ALL);
X+#undef ALL
X+    if (code)
X+    {
X+        sprintf(PQerrormsg,
X+            "pg_krb5_sendauth: krb5_auth_con_genaddrs: %s\n",
X+            error_message(code));
X+        fputs(PQerrormsg, stderr);
X+        krb5_free_principal(mycontext, server);
X+        krb5_auth_con_free(mycontext, authctx);
X         return STATUS_ERROR;
X     }
X
X+
X+    /*
X+     * krb5_sendauth does not appreciate getting a non-blocking file
X+     * descriptor.  So, we set it to blocking mode and then reset
X+     * it afterwards.
X+     */
X+    sflags = fcntl(sock, F_GETFL, 0);
X+    fcntl(sock, F_SETFL, sflags & ~O_NONBLOCK);
X+
X     /*
X      * The only thing we want back from krb5_sendauth is an error status
X-     * and any error messages.
X+     * and any error messages.  If we cared, we could get the session
X+     * key(s) from the auth context and stick them somewhere to encrypt
X+     * the whole data stream.  However, this would mean a major change
X+     * in the protocol, and I'm not prepared to do that right now.
X+     * (It should work to encrypt the session using SSL, if a bit of a
X+     * muchness.)
X      */
X-    if (code = krb5_sendauth((krb5_pointer) & sock,
X-                             PG_KRB5_VERSION,
X-                             client,
X-                             server,
X-                             (krb5_flags) 0,
X-                             (krb5_checksum *) NULL,
X-                             (krb5_creds *) NULL,
X-                             ccache,
X-                             (krb5_int32 *) NULL,
X-                             (krb5_keyblock **) NULL,
X-                             &error,
X-                             (krb5_ap_rep_enc_part **) NULL))
X+    code = krb5_sendauth(mycontext, &authctx, (krb5_pointer) &sock,
X+                 PG_KRB5_VERSION, (krb5_principal) NULL,
X+                 server, AP_OPTS_MUTUAL_REQUIRED,
X+                 (krb5_data *) NULL, (krb5_creds *) NULL,
X+                 ccache, &error, (krb5_ap_rep_enc_part **) NULL,
X+                 (krb5_creds **) NULL);
X+    fcntl(sock, F_SETFL, sflags);
X+    if (code)
X     {
X         if ((code == KRB5_SENDAUTH_REJECTED) && error)
X         {
X-            (void) sprintf(PQerrormsg,
X-                  "pg_krb5_sendauth: authentication rejected: \"%*s\"\n",
X-                           error->text.length, error->text.data);
X+            sprintf(PQerrormsg,
X+                "pg_krb5_sendauth: authentication rejected: \"%.*s\"\n",
X+                error->text.length, error->text.data);
X         }
X         else
X         {
X-            (void) sprintf(PQerrormsg,
X-                           "pg_krb5_sendauth: Kerberos error %d in krb5_sendauth\n", code);
X-            com_err("pg_krb5_sendauth", code, "in krb5_sendauth");
X+            sprintf(PQerrormsg,
X+                "pg_krb5_sendauth: krb5_sendauth: %s\n",
X+                error_message(code));
X         }
X     }
X-    krb5_free_principal(client);
X-    krb5_free_principal(server);
X+    krb5_free_principal(mycontext, server);
X+    if (error != 0)
X+        krb5_free_error(mycontext, error);
X+    krb5_auth_con_free(mycontext, authctx);
X     return code ? STATUS_ERROR : STATUS_OK;
X }
END-of-patch-bh
exit



pgsql-patches by date:

Previous
From: Chris
Date:
Subject: Re: Inherited column patches
Next
From: Pete Forman
Date:
Subject: Minor make bug on AIX