Re: Preliminary GSSAPI Patches - Mailing list pgsql-patches

From Magnus Hagander
Subject Re: Preliminary GSSAPI Patches
Date
Msg-id 467D17AF.5020500@hagander.net
Whole thread Raw
In response to Re: Preliminary GSSAPI Patches  (Magnus Hagander <magnus@hagander.net>)
List pgsql-patches
Magnus Hagander wrote:
> Stephen Frost wrote:
>> * Henry B. Hotz (hbhotz@oxy.edu) wrote:
>>> On Jun 22, 2007, at 9:56 AM, Magnus Hagander wrote:
>>>> Most likely it's just checking the keytab to find a principal with the
>>>> same name as the one presented from the client. Since one is
>>>> present, it
>>>> loads it up automatically, and verifies against it.
>>> Bingo!
>>>
>>> The server uses the keytab to decrypt the token provided by the
>>> client.  By using the GSS_C_NO_CREDENTIAL arg on the server anything
>>> put in the keytab is OK.  (The server doesn't need to authenticate
>>> itself to Kerberos, it just accepts authentication.  Mutual
>>> authentication is done using the same keys.)  The documentation needs
>>> to reflect that.
>> I agree there's some disconnect there between the documentation and the
>> apparent implementation but I'm not sure I'm in favor of changing the
>> documentation on this one.  Personally, I'd rather it return an error if
>> someone tries to use GSS_C_NO_CREDENTIAL when accepting a context than
>> to just be happy using anything in the keytab.
>
> How about doing both, then? Set the principal name if it's specified in
> the config file. If it's explicitly set to an empty string, use
> GSS_C_NO_CREDENTIAL. Seems straightforward enough to me, and shouldn't
> be hard to implement.

Here's an updated patch that does this.

//Magnus
diff -cr pgsql.orig/src/backend/libpq/auth.c pgsql/src/backend/libpq/auth.c
*** pgsql.orig/src/backend/libpq/auth.c    2007-02-08 05:52:18.000000000 +0100
--- pgsql/src/backend/libpq/auth.c    2007-06-23 14:42:45.000000000 +0200
***************
*** 23,28 ****
--- 23,29 ----
  #endif
  #include <netinet/in.h>
  #include <arpa/inet.h>
+ #include <unistd.h>

  #include "libpq/auth.h"
  #include "libpq/crypt.h"
***************
*** 295,300 ****
--- 296,611 ----
  }
  #endif   /* KRB5 */

+ #ifdef ENABLE_GSS
+ /*----------------------------------------------------------------
+  * GSSAPI authentication system
+  *----------------------------------------------------------------
+  */
+
+ #include <gssapi/gssapi.h>
+
+ #ifdef WIN32
+ /*
+  * MIT Kerberos GSSAPI DLL doesn't properly export the symbols
+  * that contain the OIDs required. Redefine here, values copied
+  * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c
+  */
+ static const gss_OID_desc GSS_C_NT_USER_NAME_desc =
+  {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"};
+ static GSS_DLLIMP gss_OID GSS_C_NT_USER_NAME = &GSS_C_NT_USER_NAME_desc;
+ #endif
+
+
+ static void
+ pg_GSS_error(int severity, char *text, OM_uint32 maj_stat, OM_uint32 min_stat)
+ {
+     gss_buffer_desc    gmsg;
+     OM_uint32        lmaj_s, lmin_s, msg_ctx;
+     char            localmsg1[128],
+                     localmsg2[128];
+
+     /* Fetch major status message */
+     msg_ctx = 0;
+     lmaj_s = gss_display_status(&lmin_s, maj_stat, GSS_C_GSS_CODE,
+             GSS_C_NO_OID, &msg_ctx, &gmsg);
+     strlcpy(localmsg1, gmsg.value, sizeof(localmsg1));
+     gss_release_buffer(&lmin_s, &gmsg);
+
+     if (msg_ctx)
+         /* More than one message available.
+          * XXX: Should we loop and read all messages?
+          * (same below)
+          */
+         ereport(WARNING,
+                 (errmsg_internal("incomplete GSS error report")));
+
+     /* Fetch mechanism minor status message */
+     msg_ctx = 0;
+     lmaj_s = gss_display_status(&lmin_s, min_stat, GSS_C_MECH_CODE,
+             GSS_C_NO_OID, &msg_ctx, &gmsg);
+     strlcpy(localmsg2, gmsg.value, sizeof(localmsg2));
+     gss_release_buffer(&lmin_s, &gmsg);
+
+     if (msg_ctx)
+         ereport(WARNING,
+                 (errmsg_internal("incomplete GSS minor error report")));
+
+     /* errmsg_internal, since translation of the first part must be
+      * done before calling this function anyway. */
+     ereport(severity,
+             (errmsg_internal("%s:%s\n%s", text, localmsg1, localmsg2)));
+ }
+
+ static int
+ pg_GSS_recvauth(Port *port)
+ {
+     OM_uint32        maj_stat, min_stat, lmin_s, gflags;
+     char           *kt_path;
+     int                mtype;
+     int                n_eq;
+     StringInfoData    buf;
+     gss_buffer_desc    gbuf;
+     gss_name_t        gnbuf;
+
+     if (pg_krb_server_keyfile && strlen(pg_krb_server_keyfile) > 0)
+     {
+         /*
+          * Set default Kerberos keytab file for the Krb5 mechanism.
+          *
+          * setenv("KRB5_KTNAME", pg_krb_server_keyfile, 0);
+          *        except setenv() not always available.
+          */
+         if (!getenv("KRB5_KTNAME"))
+         {
+             kt_path = palloc(PATH_MAX + 13);
+             snprintf(kt_path, PATH_MAX + 13,
+                     "KRB5_KTNAME=%s", pg_krb_server_keyfile);
+             putenv(kt_path);
+         }
+     }
+
+     if (pg_krb_srvnam && strlen(pg_krb_srvnam) > 0)
+     {
+         /*
+          * Load service principal credentials
+          */
+         char   *hostname;
+         int        len;
+
+         if (!pg_krb_server_hostname || !strlen(pg_krb_server_hostname))
+         {
+             char localhost[NI_MAXHOST];
+
+             /*
+              * hostname not specified in config file, so get it from
+              * the system default.
+              */
+             localhost[NI_MAXHOST-1] = '\0';
+             if (gethostname(localhost, NI_MAXHOST-1))
+                 ereport(ERROR,
+                         (errmsg_internal("gethostname for GSSAPI service principal failed")));
+             hostname = localhost;
+         }
+         else
+             hostname = pg_krb_server_hostname;
+
+         len = strlen(hostname) + strlen(pg_krb_srvnam) + 2;
+         gbuf.value = palloc(len);
+         snprintf(gbuf.value, len, "%s@%s", pg_krb_srvnam, hostname);
+         gbuf.length = strlen(gbuf.value);
+
+         ereport(DEBUG4,
+                 (errmsg_internal("Acquiring GSSAPI service credentials for %s", (char *)gbuf.value)));
+
+         maj_stat = gss_import_name(&min_stat, &gbuf,
+                 GSS_C_NT_HOSTBASED_SERVICE, &gnbuf);
+         pfree(gbuf.value);
+         if (maj_stat != GSS_S_COMPLETE)
+             pg_GSS_error(ERROR, gettext_noop("importing GSS service principal name failed"), maj_stat, min_stat);
+
+         maj_stat = gss_acquire_cred(&min_stat,
+                 gnbuf,
+                 GSS_C_INDEFINITE,
+                 GSS_C_NO_OID_SET,
+                 GSS_C_ACCEPT,
+                 &port->gss->cred,
+                 NULL,
+                 NULL);
+         if (maj_stat != GSS_S_COMPLETE)
+             pg_GSS_error(ERROR, gettext_noop("acquiring GSS service principal credentials failed"), maj_stat,
min_stat);
+
+         /*
+          * Clean up the name now that we have the credentials
+          */
+         gss_release_name(&min_stat, &gnbuf);
+     }
+     else
+     {
+         /*
+          * No service principal name specified, so accept anything
+          * the client uses (must still be present in the keytab).
+          */
+         port->gss->cred = GSS_C_NO_CREDENTIAL;
+     }
+
+     port->gss->ctx = GSS_C_NO_CONTEXT;
+
+     /*
+      * Loop through GSSAPI message exchange. This exchange can consist
+      * of multiple messags sent in both directions. First message is always
+      * from the client. All messages from client to server are password
+      * packets (type 'p').
+      */
+     do
+     {
+         mtype = pq_getbyte();
+         if (mtype != 'p')
+         {
+             /* Only log error if client didn't disconnect. */
+             if (mtype != EOF)
+                 ereport(COMMERROR,
+                         (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                          errmsg("expected GSS response, got message type %d",
+                              mtype)));
+             return STATUS_ERROR;
+         }
+
+         /* Get the actual GSS token */
+         initStringInfo(&buf);
+         if (pq_getmessage(&buf, 2000))
+         {
+             /* EOF - pq_getmessage already logged error */
+             pfree(buf.data);
+             return STATUS_ERROR;
+         }
+
+         /* Map to GSSAPI style buffer */
+         gbuf.length = buf.len;
+         gbuf.value = buf.data;
+
+         ereport(DEBUG4,
+                 (errmsg_internal("Processing received GSS token of length: %u",
+                                  gbuf.length)));
+
+         maj_stat = gss_accept_sec_context(
+                 &min_stat,
+                 &port->gss->ctx,
+                 port->gss->cred,
+                 &gbuf,
+                 GSS_C_NO_CHANNEL_BINDINGS,
+                 &port->gss->name,
+                 NULL,
+                 &port->gss->outbuf,
+                 &gflags,
+                 NULL,
+                 NULL);
+
+         /* gbuf no longer used */
+         pfree(buf.data);
+
+         ereport(DEBUG5,
+                 (errmsg_internal("gss_accept_sec_context major: %i, "
+                                  "minor: %i, outlen: %u, outflags: %x",
+                                  maj_stat, min_stat,
+                                  port->gss->outbuf.length, gflags)));
+
+         if (port->gss->outbuf.length != 0)
+         {
+             /*
+              * Negotiation generated data to be sent to the client.
+              */
+             ereport(DEBUG4,
+                     (errmsg_internal("sending GSS response token of length %u",
+                                      port->gss->outbuf.length)));
+             sendAuthRequest(port, AUTH_REQ_GSS_CONT);
+         }
+
+         if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
+         {
+             OM_uint32    lmin_s;
+             gss_delete_sec_context(&lmin_s, &port->gss->ctx, GSS_C_NO_BUFFER);
+             pg_GSS_error(ERROR,
+                     gettext_noop("accepting GSS security context failed"),
+                     maj_stat, min_stat);
+         }
+
+         if (maj_stat == GSS_S_CONTINUE_NEEDED)
+             ereport(DEBUG4,
+                     (errmsg_internal("GSS continue needed")));
+
+     } while (maj_stat == GSS_S_CONTINUE_NEEDED);
+
+     if (port->gss->cred != GSS_C_NO_CREDENTIAL)
+     {
+         /*
+          * Release service principal credentials
+          */
+         gss_release_cred(&min_stat, port->gss->cred);
+     }
+
+     /*
+      * GSS_S_COMPLETE indicates that authentication is now complete.
+      *
+      * Get the name of the user that authenticated, and compare it to the
+      * pg username that was specified for the connection.
+      */
+     maj_stat = gss_display_name(&min_stat, port->gss->name, &gbuf, NULL);
+     ereport(DEBUG1,
+             (errmsg("GSSAPI authenticated name: %s", (char *)gbuf.value)));
+     gss_release_buffer(&lmin_s, &gbuf);
+
+     /* Convert pg username to GSSAPI format */
+     gbuf.value = port->user_name;
+     gbuf.length = strlen(buf.data) + 1;
+     maj_stat = gss_import_name(&min_stat, &gbuf, GSS_C_NT_USER_NAME, &gnbuf);
+     if (maj_stat != GSS_S_COMPLETE)
+         pg_GSS_error(ERROR, "importing GSS username failed",
+                 maj_stat, min_stat);
+
+     /* Verify that usernames are identical */
+     maj_stat = gss_compare_name(&min_stat, port->gss->name, gnbuf, &n_eq);
+     if (maj_stat != GSS_S_COMPLETE)
+         pg_GSS_error(ERROR, "comparing GSS username failed",
+                 maj_stat, min_stat);
+
+     if (!n_eq)
+     {
+         /* GSS name and PGUSER are not equivalent */
+         char *namecopy;
+
+         maj_stat = gss_display_name(&min_stat, gnbuf, &gbuf, NULL);
+         if (maj_stat != GSS_S_COMPLETE)
+             pg_GSS_error(ERROR,
+                     "displaying GSS form of PGUSER failed",
+                     maj_stat, min_stat);
+
+         namecopy = palloc(gbuf.length);
+         strlcpy(namecopy, gbuf.value, gbuf.length);
+         gss_release_buffer(&lmin_s, &gbuf);
+         gss_release_name(&lmin_s, &gnbuf);
+
+         ereport(ERROR,
+                 (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
+                  errmsg("provided username and GSSAPI username don't match"),
+                  errdetail("provided: %s, GSSAPI: %s",
+                      port->user_name, namecopy)));
+     }
+     gss_release_name(&lmin_s, &gnbuf);
+
+     return STATUS_OK;
+ }
+
+ #else    /* no ENABLE_GSS */
+ static int
+ pg_GSS_recvauth(Port *port)
+ {
+     ereport(LOG,
+             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+              errmsg("GSSAPI not implemented on this server.")));
+     return STATUS_ERROR;
+ }
+ #endif    /* ENABLE_GSS */
+

  /*
   * Tell the user the authentication failed, but not (much about) why.
***************
*** 334,339 ****
--- 645,653 ----
          case uaKrb5:
              errstr = gettext_noop("Kerberos 5 authentication failed for user \"%s\"");
              break;
+         case uaGSS:
+             errstr = gettext_noop("GSSAPI authentication failed for user \"%s\"");
+             break;
          case uaTrust:
              errstr = gettext_noop("\"trust\" authentication failed for user \"%s\"");
              break;
***************
*** 429,434 ****
--- 743,753 ----
              status = pg_krb5_recvauth(port);
              break;

+         case uaGSS:
+             sendAuthRequest(port, AUTH_REQ_GSS);
+             status = pg_GSS_recvauth(port);
+             break;
+
          case uaIdent:

              /*
***************
*** 518,523 ****
--- 837,860 ----
      else if (areq == AUTH_REQ_CRYPT)
          pq_sendbytes(&buf, port->cryptSalt, 2);

+ #ifdef ENABLE_GSS
+     /* Add the authentication data for the next step of
+      * the GSSAPI negotiation. */
+     else if (areq == AUTH_REQ_GSS_CONT)
+     {
+         if (port->gss->outbuf.length > 0)
+         {
+             OM_uint32    lmin_s;
+
+             ereport(DEBUG4,
+                     (errmsg_internal("sending GSS token of length %u",
+                                      port->gss->outbuf.length)));
+             pq_sendbytes(&buf, port->gss->outbuf.value, port->gss->outbuf.length);
+             gss_release_buffer(&lmin_s, &port->gss->outbuf);
+         }
+     }
+ #endif
+
      pq_endmessage(&buf);

      /*
diff -cr pgsql.orig/src/backend/libpq/hba.c pgsql/src/backend/libpq/hba.c
*** pgsql.orig/src/backend/libpq/hba.c    2007-02-10 15:58:54.000000000 +0100
--- pgsql/src/backend/libpq/hba.c    2007-06-17 18:01:31.000000000 +0200
***************
*** 602,607 ****
--- 602,609 ----
          *userauth_p = uaPassword;
      else if (strcmp(token, "krb5") == 0)
          *userauth_p = uaKrb5;
+     else if (strcmp(token, "gss") == 0)
+         *userauth_p = uaGSS;
      else if (strcmp(token, "reject") == 0)
          *userauth_p = uaReject;
      else if (strcmp(token, "md5") == 0)
diff -cr pgsql.orig/src/backend/libpq/pg_hba.conf.sample pgsql/src/backend/libpq/pg_hba.conf.sample
*** pgsql.orig/src/backend/libpq/pg_hba.conf.sample    2006-10-12 01:01:46.000000000 +0200
--- pgsql/src/backend/libpq/pg_hba.conf.sample    2007-06-17 18:16:27.000000000 +0200
***************
*** 34,40 ****
  # the number of significant bits in the mask.  Alternatively, you can write
  # an IP address and netmask in separate columns to specify the set of hosts.
  #
! # METHOD can be "trust", "reject", "md5", "crypt", "password",
  # "krb5", "ident", "pam" or "ldap".  Note that "password" sends passwords
  # in clear text; "md5" is preferred since it sends encrypted passwords.
  #
--- 34,40 ----
  # the number of significant bits in the mask.  Alternatively, you can write
  # an IP address and netmask in separate columns to specify the set of hosts.
  #
! # METHOD can be "trust", "reject", "md5", "crypt", "password", "gss",
  # "krb5", "ident", "pam" or "ldap".  Note that "password" sends passwords
  # in clear text; "md5" is preferred since it sends encrypted passwords.
  #
diff -cr pgsql.orig/src/backend/libpq/pqcomm.c pgsql/src/backend/libpq/pqcomm.c
*** pgsql.orig/src/backend/libpq/pqcomm.c    2007-06-04 13:59:20.000000000 +0200
--- pgsql/src/backend/libpq/pqcomm.c    2007-06-22 12:48:24.000000000 +0200
***************
*** 173,178 ****
--- 173,188 ----
  {
      if (MyProcPort != NULL)
      {
+ #ifdef ENABLE_GSS
+         OM_uint32    min_s;
+         /* Shutdown GSSAPI layer */
+         if (MyProcPort->gss->ctx)
+             gss_delete_sec_context(&min_s, MyProcPort->gss->ctx, NULL);
+
+         if (MyProcPort->gss->cred)
+             gss_release_cred(&min_s, MyProcPort->gss->cred);
+ #endif
+
          /* Cleanly shut down SSL layer */
          secure_close(MyProcPort);

diff -cr pgsql.orig/src/backend/postmaster/postmaster.c pgsql/src/backend/postmaster/postmaster.c
*** pgsql.orig/src/backend/postmaster/postmaster.c    2007-03-22 20:53:30.000000000 +0100
--- pgsql/src/backend/postmaster/postmaster.c    2007-06-17 17:36:49.000000000 +0200
***************
*** 1726,1731 ****
--- 1726,1738 ----
          RandomSalt(port->cryptSalt, port->md5Salt);
      }

+     /*
+      * Allocate GSSAPI specific state struct
+      */
+ #ifdef ENABLE_GSS
+     port->gss = (pg_gssinfo *)calloc(1, sizeof(pg_gssinfo));
+ #endif
+
      return port;
  }

***************
*** 1739,1744 ****
--- 1746,1753 ----
  #ifdef USE_SSL
      secure_close(conn);
  #endif
+     if (conn->gss)
+         free(conn->gss);
      free(conn);
  }

diff -cr pgsql.orig/src/include/libpq/hba.h pgsql/src/include/libpq/hba.h
*** pgsql.orig/src/include/libpq/hba.h    2006-11-05 23:42:10.000000000 +0100
--- pgsql/src/include/libpq/hba.h    2007-06-17 18:01:47.000000000 +0200
***************
*** 22,28 ****
      uaIdent,
      uaPassword,
      uaCrypt,
!     uaMD5
  #ifdef USE_PAM
      ,uaPAM
  #endif   /* USE_PAM */
--- 22,29 ----
      uaIdent,
      uaPassword,
      uaCrypt,
!     uaMD5,
!     uaGSS,
  #ifdef USE_PAM
      ,uaPAM
  #endif   /* USE_PAM */
diff -cr pgsql.orig/src/include/libpq/libpq-be.h pgsql/src/include/libpq/libpq-be.h
*** pgsql.orig/src/include/libpq/libpq-be.h    2007-01-05 23:19:55.000000000 +0100
--- pgsql/src/include/libpq/libpq-be.h    2007-06-17 18:03:12.000000000 +0200
***************
*** 29,34 ****
--- 29,38 ----
  #include <netinet/tcp.h>
  #endif

+ #ifdef ENABLE_GSS
+ #include <gssapi/gssapi.h>
+ #endif
+
  #include "libpq/hba.h"
  #include "libpq/pqcomm.h"
  #include "utils/timestamp.h"
***************
*** 39,44 ****
--- 43,62 ----
      CAC_OK, CAC_STARTUP, CAC_SHUTDOWN, CAC_RECOVERY, CAC_TOOMANY
  } CAC_state;

+
+ /*
+  * GSSAPI specific state information
+  */
+ #ifdef ENABLE_GSS
+ typedef struct
+ {
+     gss_cred_id_t    cred;        /* GSSAPI connection cred's */
+     gss_ctx_id_t    ctx;        /* GSSAPI connection context */
+     gss_name_t        name;        /* GSSAPI client name */
+     gss_buffer_desc    outbuf;        /* GSSAPI output token buffer */
+ } pg_gssinfo;
+ #endif
+
  /*
   * This is used by the postmaster in its communication with frontends.    It
   * contains all state information needed during this communication before the
***************
*** 98,103 ****
--- 116,132 ----
      int            keepalives_interval;
      int            keepalives_count;

+ #ifdef ENABLE_GSS
+     /*
+      * If GSSAPI is supported, store GSSAPI information.
+      * Oterwise, store a NULL pointer to make sure offsets
+      * in the struct remain the same.
+      */
+     pg_gssinfo *gss;
+ #else
+     void       *gss;
+ #endif
+
      /*
       * SSL structures (keep these last so that USE_SSL doesn't affect
       * locations of other fields)
diff -cr pgsql.orig/src/include/libpq/pqcomm.h pgsql/src/include/libpq/pqcomm.h
*** pgsql.orig/src/include/libpq/pqcomm.h    2007-01-05 23:19:55.000000000 +0100
--- pgsql/src/include/libpq/pqcomm.h    2007-06-19 22:01:08.000000000 +0200
***************
*** 156,161 ****
--- 156,163 ----
  #define AUTH_REQ_CRYPT        4    /* crypt password */
  #define AUTH_REQ_MD5        5    /* md5 password */
  #define AUTH_REQ_SCM_CREDS    6    /* transfer SCM credentials */
+ #define AUTH_REQ_GSS        7    /* GSSAPI without wrap() */
+ #define AUTH_REQ_GSS_CONT    8    /* Continue GSS exchanges */

  typedef uint32 AuthRequest;

diff -cr pgsql.orig/src/include/pg_config.h.in pgsql/src/include/pg_config.h.in
*** pgsql.orig/src/include/pg_config.h.in    2007-05-04 17:20:52.000000000 +0200
--- pgsql/src/include/pg_config.h.in    2007-06-17 18:32:10.000000000 +0200
***************
*** 568,573 ****
--- 568,576 ----
  /* Define to the appropriate snprintf format for 64-bit ints, if any. */
  #undef INT64_FORMAT

+ /* Define to build with GSSAPI support. (--with-gssapi) */
+ #undef ENABLE_GSS
+
  /* Define to build with Kerberos 5 support. (--with-krb5) */
  #undef KRB5

diff -cr pgsql.orig/src/interfaces/libpq/Makefile pgsql/src/interfaces/libpq/Makefile
*** pgsql.orig/src/interfaces/libpq/Makefile    2007-01-07 09:49:31.000000000 +0100
--- pgsql/src/interfaces/libpq/Makefile    2007-06-19 15:14:31.000000000 +0200
***************
*** 57,63 ****
  # shared library link.  (The order in which you list them here doesn't
  # matter.)
  ifneq ($(PORTNAME), win32)
! SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lssl -lsocket -lnsl -lresolv -lintl,
$(LIBS))$(LDAP_LIBS_FE) $(PTHREAD_LIBS) 
  else
  SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lssl -lsocket -lnsl -lresolv -lintl
$(PTHREAD_LIBS),$(LIBS)) $(LDAP_LIBS_FE) 
  endif
--- 57,63 ----
  # shared library link.  (The order in which you list them here doesn't
  # matter.)
  ifneq ($(PORTNAME), win32)
! SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lgssapi_krb5 -lssl -lsocket -lnsl -lresolv
-lintl,$(LIBS)) $(LDAP_LIBS_FE) $(PTHREAD_LIBS) 
  else
  SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lssl -lsocket -lnsl -lresolv -lintl
$(PTHREAD_LIBS),$(LIBS)) $(LDAP_LIBS_FE) 
  endif
diff -cr pgsql.orig/src/interfaces/libpq/fe-auth.c pgsql/src/interfaces/libpq/fe-auth.c
*** pgsql.orig/src/interfaces/libpq/fe-auth.c    2007-02-10 15:58:55.000000000 +0100
--- pgsql/src/interfaces/libpq/fe-auth.c    2007-06-22 12:56:47.000000000 +0200
***************
*** 313,318 ****
--- 313,494 ----
  }
  #endif   /* KRB5 */

+ #ifdef ENABLE_GSS
+ /*
+  * GSSAPI authentication system.
+  */
+ #include <gssapi/gssapi.h>
+
+ #ifdef WIN32
+ /*
+  * MIT Kerberos GSSAPI DLL doesn't properly export the symbols
+  * that contain the OIDs required. Redefine here, values copied
+  * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c
+  */
+ static const gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_desc =
+  {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"};
+ static GSS_DLLIMP gss_OID GSS_C_NT_HOSTBASED_SERVICE = &GSS_C_NT_HOSTBASED_SERVICE_desc;
+ #endif
+
+ /*
+  * Fetch all errors of a specific type that fit into a buffer
+  * and append them.
+  */
+ static void
+ pg_GSS_error_int(char *mprefix, char *msg, int msglen,
+                  OM_uint32 stat, int type)
+ {
+     int                curlen = 0;
+     OM_uint32        lmaj_s, lmin_s;
+     gss_buffer_desc    lmsg;
+     OM_uint32        msg_ctx = 0;
+
+     do
+     {
+         lmaj_s = gss_display_status(&lmin_s, stat, type,
+                 GSS_C_NO_OID, &msg_ctx, &lmsg);
+
+         if (curlen < msglen)
+         {
+             snprintf(msg + curlen, msglen - curlen, "%s: %s\n",
+                     mprefix, (char *)lmsg.value);
+             curlen += lmsg.length;
+         }
+         gss_release_buffer(&lmin_s, &lmsg);
+     } while (msg_ctx);
+ }
+
+ /*
+  * GSSAPI errors contains two parts. Put as much as possible of
+  * both parts into the string.
+  */
+ void
+ pg_GSS_error(char *mprefix, char *msg, int msglen,
+     OM_uint32 maj_stat, OM_uint32 min_stat)
+ {
+     int mlen;
+
+     /* Fetch major error codes */
+     pg_GSS_error_int(mprefix, msg, msglen, maj_stat, GSS_C_GSS_CODE);
+     mlen = strlen(msg);
+
+     /* If there is room left, try to add the minor codes as well */
+     if (mlen < msglen-1)
+         pg_GSS_error_int(mprefix, msg + mlen, msglen - mlen,
+                 min_stat, GSS_C_MECH_CODE);
+ }
+
+ /*
+  * Continue GSS authentication with next token as needed.
+  */
+ static int
+ pg_GSS_continue(char *PQerrormsg, PGconn *conn)
+ {
+     OM_uint32    maj_stat, min_stat, lmin_s;
+
+     maj_stat = gss_init_sec_context(&min_stat,
+             GSS_C_NO_CREDENTIAL,
+             &conn->gctx,
+             conn->gtarg_nam,
+             GSS_C_NO_OID,
+             conn->gflags,
+             0,
+             GSS_C_NO_CHANNEL_BINDINGS,
+             (conn->gctx==GSS_C_NO_CONTEXT)?GSS_C_NO_BUFFER:&conn->ginbuf,
+             NULL,
+             &conn->goutbuf,
+             NULL,
+             NULL);
+
+     if (conn->gctx != GSS_C_NO_CONTEXT)
+     {
+         free(conn->ginbuf.value);
+         conn->ginbuf.value = NULL;
+         conn->ginbuf.length = 0;
+     }
+
+     if (conn->goutbuf.length != 0)
+     {
+         /*
+          * GSS generated data to send to the server. We don't care if it's
+          * the first or subsequent packet, just send the same kind of
+          * password packet.
+          */
+         if (pqPacketSend(conn, 'p',
+                     conn->goutbuf.value, conn->goutbuf.length)
+                 != STATUS_OK)
+         {
+             gss_release_buffer(&lmin_s, &conn->goutbuf);
+             return STATUS_ERROR;
+         }
+     }
+     gss_release_buffer(&lmin_s, &conn->goutbuf);
+
+     if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
+     {
+         pg_GSS_error(libpq_gettext("GSSAPI continuation error"),
+                 PQerrormsg, PQERRORMSG_LENGTH,
+                 maj_stat, min_stat);
+         gss_release_name(&lmin_s, &conn->gtarg_nam);
+         if (conn->gctx)
+             gss_delete_sec_context(&lmin_s, &conn->gctx, GSS_C_NO_BUFFER);
+         return STATUS_ERROR;
+     }
+
+     if (maj_stat == GSS_S_COMPLETE)
+         gss_release_name(&lmin_s, &conn->gtarg_nam);
+
+     return STATUS_OK;
+ }
+
+ /*
+  * Send initial GSS authentication token
+  */
+ static int
+ pg_GSS_startup(char *PQerrormsg, PGconn *conn)
+ {
+     OM_uint32    maj_stat, min_stat;
+     int            maxlen;
+     gss_buffer_desc    temp_gbuf;
+
+     if (conn->gctx)
+     {
+         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+                 libpq_gettext("duplicate GSS auth request\n"));
+         return STATUS_ERROR;
+     }
+
+     /*
+      * Import service principal name so the proper ticket can be
+      * acquired by the GSSAPI system.
+      */
+     maxlen = NI_MAXHOST + strlen(conn->krbsrvname) + 2;
+     temp_gbuf.value = (char*)malloc(maxlen);
+     snprintf(temp_gbuf.value, maxlen, "%s@%s",
+             conn->krbsrvname, conn->pghost);
+     temp_gbuf.length = strlen(temp_gbuf.value);
+
+     maj_stat = gss_import_name(&min_stat, &temp_gbuf,
+             GSS_C_NT_HOSTBASED_SERVICE, &conn->gtarg_nam);
+     free(temp_gbuf.value);
+
+     if (maj_stat != GSS_S_COMPLETE)
+     {
+         pg_GSS_error(libpq_gettext("GSSAPI name import error"),
+                 PQerrormsg, PQERRORMSG_LENGTH,
+                 maj_stat, min_stat);
+         return STATUS_ERROR;
+     }
+
+     /*
+      * Initial packet is the same as a continuation packet with
+      * no initial context.
+      */
+     conn->gctx = GSS_C_NO_CONTEXT;
+
+     return pg_GSS_continue(PQerrormsg, conn);
+ }
+ #endif

  /*
   * Respond to AUTH_REQ_SCM_CREDS challenge.
***************
*** 479,484 ****
--- 655,691 ----
              return STATUS_ERROR;
  #endif

+ #ifdef ENABLE_GSS
+         case AUTH_REQ_GSS:
+             pglock_thread();
+             if (pg_GSS_startup(PQerrormsg, conn) != STATUS_OK)
+             {
+                 /* PQerrormsg already filled in. */
+                 pgunlock_thread();
+                 return STATUS_ERROR;
+             }
+             pgunlock_thread();
+             break;
+
+         case AUTH_REQ_GSS_CONT:
+             pglock_thread();
+             if (pg_GSS_continue(PQerrormsg, conn) != STATUS_OK)
+             {
+                 /* PQerrormsg already filled in. */
+                 pgunlock_thread();
+                 return STATUS_ERROR;
+             }
+             pgunlock_thread();
+             break;
+
+ #else
+         case AUTH_REQ_GSS:
+         case AUTH_REQ_GSS_CONT:
+             snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+                     libpq_gettext("GSSAPI authentication not supported\n"));
+             return STATUS_ERROR;
+ #endif
+
          case AUTH_REQ_MD5:
          case AUTH_REQ_CRYPT:
          case AUTH_REQ_PASSWORD:
diff -cr pgsql.orig/src/interfaces/libpq/fe-connect.c pgsql/src/interfaces/libpq/fe-connect.c
*** pgsql.orig/src/interfaces/libpq/fe-connect.c    2007-03-08 20:27:28.000000000 +0100
--- pgsql/src/interfaces/libpq/fe-connect.c    2007-06-19 21:24:35.000000000 +0200
***************
*** 181,188 ****
      {"sslmode", "PGSSLMODE", DefaultSSLMode, NULL,
      "SSL-Mode", "", 8},            /* sizeof("disable") == 8 */

! #ifdef KRB5
!     /* Kerberos authentication supports specifying the service name */
      {"krbsrvname", "PGKRBSRVNAME", PG_KRB_SRVNAM, NULL,
      "Kerberos-service-name", "", 20},
  #endif
--- 181,188 ----
      {"sslmode", "PGSSLMODE", DefaultSSLMode, NULL,
      "SSL-Mode", "", 8},            /* sizeof("disable") == 8 */

! #if defined(KRB5) || defined(ENABLE_GSS)
!     /* Kerberos and GSSAPI authentication support specifying the service name */
      {"krbsrvname", "PGKRBSRVNAME", PG_KRB_SRVNAM, NULL,
      "Kerberos-service-name", "", 20},
  #endif
***************
*** 412,418 ****
          conn->sslmode = strdup("require");
      }
  #endif
! #ifdef KRB5
      tmp = conninfo_getval(connOptions, "krbsrvname");
      conn->krbsrvname = tmp ? strdup(tmp) : NULL;
  #endif
--- 412,418 ----
          conn->sslmode = strdup("require");
      }
  #endif
! #if defined(KRB5) || defined(ENABLE_GSS)
      tmp = conninfo_getval(connOptions, "krbsrvname");
      conn->krbsrvname = tmp ? strdup(tmp) : NULL;
  #endif
***************
*** 1496,1507 ****

                  /*
                   * Try to validate message length before using it.
!                  * Authentication requests can't be very large.  Errors can be
                   * a little larger, but not huge.  If we see a large apparent
                   * length in an error, it means we're really talking to a
                   * pre-3.0-protocol server; cope.
                   */
!                 if (beresp == 'R' && (msgLength < 8 || msgLength > 100))
                  {
                      printfPQExpBuffer(&conn->errorMessage,
                                        libpq_gettext(
--- 1496,1508 ----

                  /*
                   * Try to validate message length before using it.
!                  * Authentication requests can't be very large, although GSS
!                  * auth requests may not be that small.  Errors can be
                   * a little larger, but not huge.  If we see a large apparent
                   * length in an error, it means we're really talking to a
                   * pre-3.0-protocol server; cope.
                   */
!                 if (beresp == 'R' && (msgLength < 8 || msgLength > 2000))
                  {
                      printfPQExpBuffer(&conn->errorMessage,
                                        libpq_gettext(
***************
*** 1660,1665 ****
--- 1661,1703 ----
                          return PGRES_POLLING_READING;
                      }
                  }
+ #ifdef ENABLE_GSS
+                 /*
+                  * AUTH_REQ_GSS provides no input data
+                  * Just set the request flags
+                  */
+                 if (areq == AUTH_REQ_GSS)
+                     conn->gflags = GSS_C_MUTUAL_FLAG;
+
+                 /*
+                  * Read GSSAPI data packets
+                  */
+                 if (areq == AUTH_REQ_GSS_CONT)
+                 {
+                     /* Continue GSSAPI authentication */
+                     int llen = msgLength - 4;
+
+                     /*
+                      * We can be called repeatedly for the same buffer.
+                      * Avoid re-allocating the buffer in this case -
+                      * just re-use the old buffer.
+                      */
+                     if (llen != conn->ginbuf.length)
+                     {
+                         if (conn->ginbuf.value)
+                             free(conn->ginbuf.value);
+
+                         conn->ginbuf.length = llen;
+                         conn->ginbuf.value = malloc(llen);
+                     }
+
+                     if (pqGetnchar(conn->ginbuf.value, llen, conn))
+                     {
+                         /* We'll come back when there is more data. */
+                         return PGRES_POLLING_READING;
+                     }
+                 }
+ #endif

                  /*
                   * OK, we successfully read the message; mark data consumed
***************
*** 1952,1958 ****
          free(conn->pgpass);
      if (conn->sslmode)
          free(conn->sslmode);
! #ifdef KRB5
      if (conn->krbsrvname)
          free(conn->krbsrvname);
  #endif
--- 1990,1996 ----
          free(conn->pgpass);
      if (conn->sslmode)
          free(conn->sslmode);
! #if defined(KRB5) || defined(GSS)
      if (conn->krbsrvname)
          free(conn->krbsrvname);
  #endif
***************
*** 1968,1973 ****
--- 2006,2024 ----
          notify = notify->next;
          free(prev);
      }
+ #ifdef ENABLE_GSS
+     {
+         OM_uint32    min_s;
+         if (conn->gctx)
+             gss_delete_sec_context(&min_s, &conn->gctx, GSS_C_NO_BUFFER);
+         if (conn->gtarg_nam)
+             gss_release_name(&min_s, &conn->gtarg_nam);
+         if (conn->ginbuf.length)
+             gss_release_buffer(&min_s, &conn->ginbuf);
+         if (conn->goutbuf.length)
+             gss_release_buffer(&min_s, &conn->goutbuf);
+     }
+ #endif
      pstatus = conn->pstatus;
      while (pstatus != NULL)
      {
diff -cr pgsql.orig/src/interfaces/libpq/libpq-int.h pgsql/src/interfaces/libpq/libpq-int.h
*** pgsql.orig/src/interfaces/libpq/libpq-int.h    2007-03-03 20:52:47.000000000 +0100
--- pgsql/src/interfaces/libpq/libpq-int.h    2007-06-17 17:40:38.000000000 +0200
***************
*** 44,49 ****
--- 44,53 ----
  /* include stuff found in fe only */
  #include "pqexpbuffer.h"

+ #ifdef ENABLE_GSS
+ #include <gssapi/gssapi.h>
+ #endif
+
  #ifdef USE_SSL
  #include <openssl/ssl.h>
  #include <openssl/err.h>
***************
*** 268,274 ****
      char       *pguser;            /* Postgres username and password, if any */
      char       *pgpass;
      char       *sslmode;        /* SSL mode (require,prefer,allow,disable) */
! #ifdef KRB5
      char       *krbsrvname;        /* Kerberos service name */
  #endif

--- 272,278 ----
      char       *pguser;            /* Postgres username and password, if any */
      char       *pgpass;
      char       *sslmode;        /* SSL mode (require,prefer,allow,disable) */
! #if defined(KRB5) || defined(GSS)
      char       *krbsrvname;        /* Kerberos service name */
  #endif

***************
*** 349,354 ****
--- 353,366 ----
      char        peer_cn[SM_USER + 1];    /* peer common name */
  #endif

+ #ifdef ENABLE_GSS
+     gss_ctx_id_t    gctx;        /* GSS context */
+     gss_name_t        gtarg_nam;    /* GSS target name */
+     OM_uint32        gflags;        /* GSS service request flags */
+     gss_buffer_desc    ginbuf;        /* GSS input token */
+     gss_buffer_desc    goutbuf;    /* GSS output token */
+ #endif
+
      /* Buffer for current error message */
      PQExpBufferData errorMessage;        /* expansible string */

***************
*** 398,403 ****
--- 410,420 ----
  #define pgunlock_thread()    ((void) 0)
  #endif

+ /* === in fe-auth.c === */
+ #ifdef ENABLE_GSS
+ extern void pg_GSS_error(char *mprefix, char *msg, int msglen,
+         OM_uint32 maj_stat, OM_uint32 min_stat);
+ #endif

  /* === in fe-exec.c === */


pgsql-patches by date:

Previous
From: Greg Smith
Date:
Subject: Re: Load Distributed Checkpoints, take 3
Next
From: "Simon Riggs"
Date:
Subject: Re: Load Distributed Checkpoints, take 3