Re: Preliminary GSSAPI Patches - Mailing list pgsql-patches

From Magnus Hagander
Subject Re: Preliminary GSSAPI Patches
Date
Msg-id 467BAB31.5040006@hagander.net
Whole thread Raw
In response to Re: Preliminary GSSAPI Patches  (Magnus Hagander <magnus@hagander.net>)
Responses Re: Preliminary GSSAPI Patches  (Stephen Frost <sfrost@snowman.net>)
List pgsql-patches
Magnus Hagander wrote:
>> Be curious to see what you've done, but if you're actively changing
>> things I'll let them settle.
>
> I've got a bit more cleanup to do, but I'm almost there.
>
> Much of it is just cleanup. I've changed the structs arond to be more in
> line with the other code around it, and such. Refacored some of the code to
> cut down duplicate codes. Added some stuff to make it work on windows
> (still just with MIT kerberos and not native though). Fixed two (I think it
> was) small memory leaks.
>
> Protocol-wise, it no longer piggybacks int eh AuthenticationOk message -
> instead we send an extra continue message followed right away by an
> AuthenticationOk one.
>
> Oh, and I've added autoconf. Not complete yet, but getting there :-)
>
> I'll post the updated patch shortly :-)

Ok. Here's the version I have right now, sans autoconf (which I broke in
my attempts to make it work with mingw).

I have one major question remaining:
We enable the setting of the service name in the server configuration
file, but we never use that variable anywhere. We do, however, use the
service name on the client, in order to pick the correct key (and
turning this off makes GSSAPI no longer work).

If this is correct, we should not enable that parameter on the server.
If it's not correct, we should be using it somewhere.

Is this perhaps a leftover from the old gssapi-encryption code? In that
we need to use that parameter on the server in order to enable
encryption, but can remove it for now, until we have that? (Since the
parameter is around for krb5 anyway, it's just #ifdefing it back out, of
course, not actually removing it)

(Still working on the documentation part)

//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-22 12:39:25.000000000 +0200
***************
*** 295,300 ****
--- 295,539 ----
  }
  #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);
+         }
+     }
+
+     port->gss->ctx = GSS_C_NO_CONTEXT;
+     port->gss->cred = GSS_C_NO_CREDENTIAL;
+
+     /*
+      * 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);
+
+     /*
+      * 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 ****
--- 573,581 ----
          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 ****
--- 671,681 ----
              status = pg_krb5_recvauth(port);
              break;

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

              /*
***************
*** 518,523 ****
--- 765,788 ----
      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:35:51.000000000 +0200
***************
*** 313,318 ****
--- 313,490 ----
  }
  #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;
+     }
+
+     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;
+     }
+
+     conn->gctx = GSS_C_NO_CONTEXT;
+
+     /*
+      * Initial packet is the same as a continuation packet with
+      * no initial context.
+      */
+     return pg_GSS_continue(PQerrormsg, conn);
+ }
+ #endif

  /*
   * Respond to AUTH_REQ_SCM_CREDS challenge.
***************
*** 479,484 ****
--- 651,687 ----
              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: Heikki Linnakangas
Date:
Subject: Re: Load Distributed Checkpoints, take 3
Next
From: "Simon Riggs"
Date:
Subject: Re: Transaction Guarantee, updated version