Thread: Preliminary GSSAPI Patches

Preliminary GSSAPI Patches

From
"Henry B. Hotz"
Date:
These patches have been reasonably tested (and cross-tested) on
Solaris 9 (SPARC) and MacOS 10.4 (both G4 and Intel) with the native
GSSAPI libraries.  They implement the gss-np and (incompletely) the
gss authentication methods.  Unlike the current krb5 method gssapi
has native support in Java and (with the SSPI) on Windows.

I still have bugs in the security layer for the gss method.
Hopefully will finish getting them ironed out today or tomorrow.

Documentation is in the README.GSSAPI file.  Make sure you get it
created when you apply the patches.



------------------------------------------------------------------------
The opinions expressed in this message are mine,
not those of Caltech, JPL, NASA, or the US Government.
Henry.B.Hotz@jpl.nasa.gov, or hbhotz@oxy.edu



Attachment

Re: Preliminary GSSAPI Patches

From
Bruce Momjian
Date:
Your patch has been added to the PostgreSQL unapplied patches list at:

    http://momjian.postgresql.org/cgi-bin/pgpatches

It will be applied as soon as one of the PostgreSQL committers reviews
and approves it.

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


Henry B. Hotz wrote:
> These patches have been reasonably tested (and cross-tested) on
> Solaris 9 (SPARC) and MacOS 10.4 (both G4 and Intel) with the native
> GSSAPI libraries.  They implement the gss-np and (incompletely) the
> gss authentication methods.  Unlike the current krb5 method gssapi
> has native support in Java and (with the SSPI) on Windows.
>
> I still have bugs in the security layer for the gss method.
> Hopefully will finish getting them ironed out today or tomorrow.
>
> Documentation is in the README.GSSAPI file.  Make sure you get it
> created when you apply the patches.
>

[ Attachment, skipping... ]

>
>
> ------------------------------------------------------------------------
> The opinions expressed in this message are mine,
> not those of Caltech, JPL, NASA, or the US Government.
> Henry.B.Hotz@jpl.nasa.gov, or hbhotz@oxy.edu
>
>
>
> ---------------------------(end of broadcast)---------------------------
> TIP 7: You can help support the PostgreSQL project by donating at
>
>                 http://www.postgresql.org/about/donate

--
  Bruce Momjian  <bruce@momjian.us>          http://momjian.us
  EnterpriseDB                               http://www.enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +

Re: Preliminary GSSAPI Patches

From
"Henry B. Hotz"
Date:
These patches are updated as discussed to remove the incomplete
feature.  Unfortunately I have a wedding to go to this weekend and
won't get them tested until next week.  Will post when I've done so.

On Mar 31, 2007, at 3:41 PM, Henry B. Hotz wrote:

> These patches have been reasonably tested (and cross-tested) on
> Solaris 9 (SPARC) and MacOS 10.4 (both G4 and Intel) with the
> native GSSAPI libraries.  They implement the gss-np and
> (incompletely) the gss authentication methods.  Unlike the current
> krb5 method gssapi has native support in Java and (with the SSPI)
> on Windows.
>
> I still have bugs in the security layer for the gss method.
> Hopefully will finish getting them ironed out today or tomorrow.
>
> Documentation is in the README.GSSAPI file.  Make sure you get it
> created when you apply the patches.
>
> <gss.patches.gz>



Just to cover the legal bases:  I don't consider these changes to be
significant enough to require the involvement of the JPL clearance
process.  JPL has already ruled that they do not fall afoul of any
ITAR restrictions.

I am not imposing any license restrictions myself either, but some
credit in the release notes, or wherever, would be appreciated.
------------------------------------------------------------------------
The opinions expressed in this message are mine,
not those of Caltech, JPL, NASA, or the US Government.
Henry.B.Hotz@jpl.nasa.gov, or hbhotz@oxy.edu



Attachment

Re: Preliminary GSSAPI Patches

From
"Henry B. Hotz"
Date:
I finally got to testing that updated patch.  It's fine per-se, but
was missing the updated README.GSSAPI file.  Herewith fixed.


On May 12, 2007, at 9:53 AM, Henry B. Hotz wrote:

> These patches are updated as discussed to remove the incomplete
> feature.  Unfortunately I have a wedding to go to this weekend and
> won't get them tested until next week.  Will post when I've done so.
>
> On Mar 31, 2007, at 3:41 PM, Henry B. Hotz wrote:
>
>> These patches have been reasonably tested (and cross-tested) on
>> Solaris 9 (SPARC) and MacOS 10.4 (both G4 and Intel) with the
>> native GSSAPI libraries.  They implement the gss-np and
>> (incompletely) the gss authentication methods.  Unlike the current
>> krb5 method gssapi has native support in Java and (with the SSPI)
>> on Windows.
>>
>> I still have bugs in the security layer for the gss method.
>> Hopefully will finish getting them ironed out today or tomorrow.
>>
>> Documentation is in the README.GSSAPI file.  Make sure you get it
>> created when you apply the patches.
>>
>> <gss.patches.gz>
>
> <gss.patch2.bz2>
>
>
> Just to cover the legal bases:  I don't consider these changes to
> be significant enough to require the involvement of the JPL
> clearance process.  JPL has already ruled that they do not fall
> afoul of any ITAR restrictions.
>
> I am not imposing any license restrictions myself either, but some
> credit in the release notes, or wherever, would be appreciated.
------------------------------------------------------------------------
The opinions expressed in this message are mine,
not those of Caltech, JPL, NASA, or the US Government.
Henry.B.Hotz@jpl.nasa.gov, or hbhotz@oxy.edu



Attachment

Re: Preliminary GSSAPI Patches

From
Bruce Momjian
Date:
Your patch has been added to the PostgreSQL unapplied patches list at:

    http://momjian.postgresql.org/cgi-bin/pgpatches

It will be applied as soon as one of the PostgreSQL committers reviews
and approves it.

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


Henry B. Hotz wrote:
> I finally got to testing that updated patch.  It's fine per-se, but
> was missing the updated README.GSSAPI file.  Herewith fixed.
>

[ Attachment, skipping... ]

>
> On May 12, 2007, at 9:53 AM, Henry B. Hotz wrote:
>
> > These patches are updated as discussed to remove the incomplete
> > feature.  Unfortunately I have a wedding to go to this weekend and
> > won't get them tested until next week.  Will post when I've done so.
> >
> > On Mar 31, 2007, at 3:41 PM, Henry B. Hotz wrote:
> >
> >> These patches have been reasonably tested (and cross-tested) on
> >> Solaris 9 (SPARC) and MacOS 10.4 (both G4 and Intel) with the
> >> native GSSAPI libraries.  They implement the gss-np and
> >> (incompletely) the gss authentication methods.  Unlike the current
> >> krb5 method gssapi has native support in Java and (with the SSPI)
> >> on Windows.
> >>
> >> I still have bugs in the security layer for the gss method.
> >> Hopefully will finish getting them ironed out today or tomorrow.
> >>
> >> Documentation is in the README.GSSAPI file.  Make sure you get it
> >> created when you apply the patches.
> >>
> >> <gss.patches.gz>
> >
> > <gss.patch2.bz2>
> >
> >
> > Just to cover the legal bases:  I don't consider these changes to
> > be significant enough to require the involvement of the JPL
> > clearance process.  JPL has already ruled that they do not fall
> > afoul of any ITAR restrictions.
> >
> > I am not imposing any license restrictions myself either, but some
> > credit in the release notes, or wherever, would be appreciated.
> ------------------------------------------------------------------------
> The opinions expressed in this message are mine,
> not those of Caltech, JPL, NASA, or the US Government.
> Henry.B.Hotz@jpl.nasa.gov, or hbhotz@oxy.edu
>
>
>
> ---------------------------(end of broadcast)---------------------------
> TIP 1: if posting/reading through Usenet, please send an appropriate
>        subscribe-nomail command to majordomo@postgresql.org so that your
>        message can get through to the mailing list cleanly

--
  Bruce Momjian  <bruce@momjian.us>          http://momjian.us
  EnterpriseDB                               http://www.enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +

Re: Preliminary GSSAPI Patches

From
Magnus Hagander
Date:
On Sun, May 20, 2007 at 01:28:40AM -0700, Henry B. Hotz wrote:
> I finally got to testing that updated patch.  It's fine per-se, but
> was missing the updated README.GSSAPI file.  Herewith fixed.
>

I've been reviewing and updating this patch, for a while now.I've changed
quite a bit around, and I have it working fine, but I have one question.

Is there a way to provoke GSSAPI into sending multiple packets in the
authentication? It doesn't seem to do that for me, and ISTM that the code
as it stands is broken in that case - but I'd like to verify it.

Basically, pg_GSS_recvauth() is supposed to loop and read all "continuing
exchange packets", right? But the reading of packets from the network sits
*outside* the loop. So it basically just loops over and over on the same
data, which ISTM is wrong. It does send a proper ask-for-continue message
to the frontend inside the loop, but I can't figure out how it's supposed
to read the response.

It looks like the fix should be as simple as moving the packet reading into
the loop, but again I'd like a way to test it :)

//Magnus


Re: Preliminary GSSAPI Patches

From
"Henry B. Hotz"
Date:
Such timing!

I just spent most of yesterday stepping though the gssapi sample
app's in Java 1.4 with someone here at work.  Was thinking I needed
to get back to the JDBC client and do what I promised.  Also finished
filtering the PG lists for stuff just before seeing this email.

On Jun 19, 2007, at 6:04 AM, Magnus Hagander wrote:

> On Sun, May 20, 2007 at 01:28:40AM -0700, Henry B. Hotz wrote:
>> I finally got to testing that updated patch.  It's fine per-se, but
>> was missing the updated README.GSSAPI file.  Herewith fixed.
>>
>
> I've been reviewing and updating this patch, for a while now.I've
> changed
> quite a bit around, and I have it working fine, but I have one
> question.

Be curious to see what you've done, but if you're actively changing
things I'll let them settle.

> Is there a way to provoke GSSAPI into sending multiple packets in the
> authentication? It doesn't seem to do that for me, and ISTM that
> the code
> as it stands is broken in that case - but I'd like to verify it.

Remember wondering about that myself.  For SASL if you turn on all
the options you get an extra round trip.  Not for GSSAPI/Krb5, which
is pretty efficient in that respect.  The loop logic for SASL is just
different enough I can imagine messing up, but I would have thought
it would have made me get the logic right.

The only thing I can think of is to use a different GSSAPI
mechanism.  That opens an interesting can of worms that has nothing
to do with Postgresql.  First of all that means you need to use a
GSSAPI implementation that supports multiple mechanisms (which
precludes Java for now).  That in turn means either Sun, or MIT 1.6+,
or Heimdal 0.8+.  Second, you need another mechanism to install.
That means the commercial Entrust SPKM mechanism on Sun, or the UMICH
SPKM mechanism, or some SPNEGO mechanism.

Love says there are problems with the SPKM RFC and the UMICH
implementation won't interoperate with other implementations as a
result (but it works with itself).  I also know he's been
experimenting with other mechanisms.  Looking at the latest Heimdal
snapshot I have, it seems to have both SPNEGO and NTLM mechanism code
in it.

A configuration that used SPNEGO to negotiate Kerberos 5 ought to
take two round trips, at least.  Feel like trying it?

I never tested my patches against a Heimdal gssapi library.  My bad.
If you try the patches against Heimdal GSSAPI/SPNEGO/Krb5 that ought
to be a really good test case.  It would be a good server case to try
with a native Windows client as well.

> Basically, pg_GSS_recvauth() is supposed to loop and read all
> "continuing
> exchange packets", right? But the reading of packets from the
> network sits
> *outside* the loop. So it basically just loops over and over on the
> same
> data, which ISTM is wrong. It does send a proper ask-for-continue
> message
> to the frontend inside the loop, but I can't figure out how it's
> supposed
> to read the response.

I remember having to stand on my head to get it (apparently) right on
the client side.  I also remember the server side was a lot simpler,
but you're saying I may have oversimplified?  I can't answer the
question about the server side without reviewing the code.

> It looks like the fix should be as simple as moving the packet
> reading into
> the loop, but again I'd like a way to test it :)

Well, probably, but that doesn't sound that simple to me (assuming I
really didn't do that to begin with).

> //Magnus

------------------------------------------------------------------------
The opinions expressed in this message are mine,
not those of Caltech, JPL, NASA, or the US Government.
Henry.B.Hotz@jpl.nasa.gov, or hbhotz@oxy.edu



Re: Preliminary GSSAPI Patches

From
Magnus Hagander
Date:
On Tue, Jun 19, 2007 at 06:19:37PM -0700, Henry B. Hotz wrote:
> Such timing!
>
> I just spent most of yesterday stepping though the gssapi sample
> app's in Java 1.4 with someone here at work.  Was thinking I needed
> to get back to the JDBC client and do what I promised.  Also finished
> filtering the PG lists for stuff just before seeing this email.

:-)

>
> >On Sun, May 20, 2007 at 01:28:40AM -0700, Henry B. Hotz wrote:
> >>I finally got to testing that updated patch.  It's fine per-se, but
> >>was missing the updated README.GSSAPI file.  Herewith fixed.
> >>
> >
> >I've been reviewing and updating this patch, for a while now.I've
> >changed
> >quite a bit around, and I have it working fine, but I have one
> >question.
>
> 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 :-)

> >Is there a way to provoke GSSAPI into sending multiple packets in the
> >authentication? It doesn't seem to do that for me, and ISTM that
> >the code
> >as it stands is broken in that case - but I'd like to verify it.
>
> Remember wondering about that myself.  For SASL if you turn on all
> the options you get an extra round trip.  Not for GSSAPI/Krb5, which
> is pretty efficient in that respect.  The loop logic for SASL is just
> different enough I can imagine messing up, but I would have thought
> it would have made me get the logic right.
>
> The only thing I can think of is to use a different GSSAPI
> mechanism.  That opens an interesting can of worms that has nothing
> to do with Postgresql.  First of all that means you need to use a
> GSSAPI implementation that supports multiple mechanisms (which
> precludes Java for now).  That in turn means either Sun, or MIT 1.6+,
> or Heimdal 0.8+.  Second, you need another mechanism to install.
> That means the commercial Entrust SPKM mechanism on Sun, or the UMICH
> SPKM mechanism, or some SPNEGO mechanism.
>
> Love says there are problems with the SPKM RFC and the UMICH
> implementation won't interoperate with other implementations as a
> result (but it works with itself).  I also know he's been
> experimenting with other mechanisms.  Looking at the latest Heimdal
> snapshot I have, it seems to have both SPNEGO and NTLM mechanism code
> in it.
>
> A configuration that used SPNEGO to negotiate Kerberos 5 ought to
> take two round trips, at least.  Feel like trying it?

Not really ;-) I did check the code, and it certaily wasn't right the way
it was. I added some manual packet injection, and it ended up in the wrong
place. It looks right now, and I'll have to test that eventually (my test
packets end up in the right place now). But what I have now *seems* right.

> >Basically, pg_GSS_recvauth() is supposed to loop and read all
> >"continuing
> >exchange packets", right? But the reading of packets from the
> >network sits
> >*outside* the loop. So it basically just loops over and over on the
> >same
> >data, which ISTM is wrong. It does send a proper ask-for-continue
> >message
> >to the frontend inside the loop, but I can't figure out how it's
> >supposed
> >to read the response.
>
> I remember having to stand on my head to get it (apparently) right on
> the client side.  I also remember the server side was a lot simpler,
> but you're saying I may have oversimplified?  I can't answer the
> question about the server side without reviewing the code.

The client side was much more complete, yes :) And with my other chanegs to
the protocol, it actually gets more than one packet.

As for the server-side fix, it was just moving a couple of lines of code
inside the loop...

//Magnus


Re: Preliminary GSSAPI Patches

From
Magnus Hagander
Date:
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 === */


Re: Preliminary GSSAPI Patches

From
Stephen Frost
Date:
* Magnus Hagander (magnus@hagander.net) wrote:
> 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.

Uh, shouldn't you be acquiring the server credentials before accepting
the context?  That'd be done using gss_acquire_cred(), which takes the
service name (in gss_name_t structure) as an argument.  That would then
be passed in to gss_accept_sec_context() instead of using
GSS_C_NO_CREDENTIAL (in port->gss->cred).  I'm kind of suprised it's
working without that and rather curious as to what it's doing under the
hood to make that happen. :/

    Thanks,

        Stephen

Attachment

Re: Preliminary GSSAPI Patches

From
Magnus Hagander
Date:
Stephen Frost wrote:
> * Magnus Hagander (magnus@hagander.net) wrote:
>> 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.
>
> Uh, shouldn't you be acquiring the server credentials before accepting
> the context?  That'd be done using gss_acquire_cred(), which takes the
> service name (in gss_name_t structure) as an argument.  That would then
> be passed in to gss_accept_sec_context() instead of using
> GSS_C_NO_CREDENTIAL (in port->gss->cred).

That's the direction I was thinking in. I just wanted to have it
confirmed. Henry, what's your take on this?

> I'm kind of suprised it's
> working without that and rather curious as to what it's doing under the
> hood to make that happen. :/

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.

//Magnus


Re: Preliminary GSSAPI Patches

From
"Henry B. Hotz"
Date:
On Jun 22, 2007, at 9:56 AM, Magnus Hagander wrote:

> Stephen Frost wrote:
>> * Magnus Hagander (magnus@hagander.net) wrote:
>>> 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.
>>
>> Uh, shouldn't you be acquiring the server credentials before
>> accepting
>> the context?  That'd be done using gss_acquire_cred(), which takes
>> the
>> service name (in gss_name_t structure) as an argument.  That would
>> then
>> be passed in to gss_accept_sec_context() instead of using
>> GSS_C_NO_CREDENTIAL (in port->gss->cred).
>
> That's the direction I was thinking in. I just wanted to have it
> confirmed. Henry, what's your take on this?
>
>> I'm kind of suprised it's
>> working without that and rather curious as to what it's doing
>> under the
>> hood to make that happen. :/
>
> 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.

The real question is whether we *need* to check anything.  If the
keytab is unique to PostgreSQL, as I think it should be, then I think
the admin should put only the right stuff in the keytab, and no
further checks are needed.

Now it *might* make sense to check that the credential that's
accepted actually has some correspondence to what ./configure said.
If we do do that, then we need to allow for the ways Microsoft mucks
with the case of the name.  (Kerberos is supposed to be case
sensitive, but Microsoft work that way.)  In particular I think we
may need both postgres/<server> and POSTGRES/<server> in the keytab
in order to support the to-be-written native Windows SSPI client at
the same time as the current Kerberos 5 and GSSAPI Unix clients.

Now what happens with non-Kerberos 5 mechansims like SPKM and
SPNEGO?  ;-)  I'll have to see, even if it doesn't matter for Java, yet.
------------------------------------------------------------------------
The opinions expressed in this message are mine,
not those of Caltech, JPL, NASA, or the US Government.
Henry.B.Hotz@jpl.nasa.gov, or hbhotz@oxy.edu



Re: Preliminary GSSAPI Patches

From
Stephen Frost
Date:
* 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.

> The real question is whether we *need* to check anything.  If the
> keytab is unique to PostgreSQL, as I think it should be, then I think
> the admin should put only the right stuff in the keytab, and no
> further checks are needed.

While I agree that in general it's best to have a keytab file for each
seperate service, there's no guarentee of that being done and using,
say, the 'host' service to allow authentication to PG when the PG
service is 'postgres' isn't right.

> Now it *might* make sense to check that the credential that's
> accepted actually has some correspondence to what ./configure said.

Or what's configured in the postgres.conf (as is done for Kerberos
now...).

> If we do do that, then we need to allow for the ways Microsoft mucks
> with the case of the name.  (Kerberos is supposed to be case
> sensitive, but Microsoft work that way.)  In particular I think we
> may need both postgres/<server> and POSTGRES/<server> in the keytab
> in order to support the to-be-written native Windows SSPI client at
> the same time as the current Kerberos 5 and GSSAPI Unix clients.

Supporting multiple, specific, keys might be an interesting challenge,
but I'm not too keen on worrying about it right now regardless.  I'd
also much rather err on the side of "overly paranoid" than "if it works,
just let it in".  If someone ends up having to support both windows SSPI
clients and unix Kerberos/GSSAPI clients it's entirely possible to
suggest they just make it POSTGRES and configure the clients
accordingly.

> Now what happens with non-Kerberos 5 mechansims like SPKM and
> SPNEGO?  ;-)  I'll have to see, even if it doesn't matter for Java, yet.

Err, SPNEGO's not really a full mechanism itself, it's got sub-mechs of
NTLM and Kerberos and from what I've seen it does the same thing SSPI
does and assumes uppercase.  Again, it's ugly, but not unbearable.  Such
fun things are probably also why mod-auth-kerb for Apache lets you
configure the service name, like most other Kerberos servers...  I've
yet to run into one that will just use any key in the keytab it's given.

    Thanks,

        Stephen

Attachment

Re: Preliminary GSSAPI Patches

From
Magnus Hagander
Date:
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.


>> If we do do that, then we need to allow for the ways Microsoft mucks
>> with the case of the name.  (Kerberos is supposed to be case
>> sensitive, but Microsoft work that way.)  In particular I think we
>> may need both postgres/<server> and POSTGRES/<server> in the keytab
>> in order to support the to-be-written native Windows SSPI client at
>> the same time as the current Kerberos 5 and GSSAPI Unix clients.
>
> Supporting multiple, specific, keys might be an interesting challenge,
> but I'm not too keen on worrying about it right now regardless.  I'd
> also much rather err on the side of "overly paranoid" than "if it works,
> just let it in".  If someone ends up having to support both windows SSPI
> clients and unix Kerberos/GSSAPI clients it's entirely possible to
> suggest they just make it POSTGRES and configure the clients
> accordingly.

Yeah, that's how we do it today with Kerberos. But it *would* be handy
if this was easier ;-)


//Magnus


Re: Preliminary GSSAPI Patches

From
Magnus Hagander
Date:
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 === */


Re: Preliminary GSSAPI Patches

From
"Magnus Hagander"
Date:
> >>> 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.
>
> I don't have a problem with that, but you'll want multiple service
> names as soon as you want to support the SSPI.
>
> Also don't get too bent out of shape about some client using the
> wrong service name.  The client *still* needs to prove who it
> represents;  there's no hole there.  The only real security issue I
> can think of is that someone who subverts the PostgreSQL server could
> steal the "host" service keys and then (with a whole bunch of other
> work) masquerade as the SSH daemon.

Ok. that's certainly a lot more narrow than I thought. you can see from my updated patch that it's not particularly
lotsof code. but if the gain is so little  
and we end up recommending people not to use that part anyway for compatibility I'm more than happy to take it out
again.You certainly know more about these  
aspect of gss than me ;)

> Don't read too much into the mod_auth_kerb situation.  The main
> reason it still takes a specific, configured service name is that
> neither Russ Allbery nor I has gotten around to submitting a proper
> patch to fix that.  The only reason it was written the way it is in
> the first place is that the ability to use GSS_C_NO_CREDENTIAL that
> way is "obscure" and most people don't know it.  I can say that with
> some confidence because Russ and I had a long discussion with Sam
> Hartman about how it ought to be done.

Ok. That makes a lot of sense then.

> I'm told that the way Apple's equivalent to mod_auth_kerb works is it
> uses GSS_C_NO_CREDENTIAL and then does a case-insensitive compare of
> the resulting match to "HTTP".  We could do the same thing, if you
> think it's worth it.

Do you know if this is documented somewhere? It's always nice with references.

/Magnus


Re: Preliminary GSSAPI Patches

From
"Henry B. Hotz"
Date:
On Jun 23, 2007, at 1:44 AM, 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.

I don't have a problem with that, but you'll want multiple service
names as soon as you want to support the SSPI.

Also don't get too bent out of shape about some client using the
wrong service name.  The client *still* needs to prove who it
represents;  there's no hole there.  The only real security issue I
can think of is that someone who subverts the PostgreSQL server could
steal the "host" service keys and then (with a whole bunch of other
work) masquerade as the SSH daemon.

>>> If we do do that, then we need to allow for the ways Microsoft mucks
>>> with the case of the name.  (Kerberos is supposed to be case
>>> sensitive, but Microsoft work that way.)  In particular I think we
>>> may need both postgres/<server> and POSTGRES/<server> in the keytab
>>> in order to support the to-be-written native Windows SSPI client at
>>> the same time as the current Kerberos 5 and GSSAPI Unix clients.
>>
>> Supporting multiple, specific, keys might be an interesting
>> challenge,
>> but I'm not too keen on worrying about it right now regardless.  I'd
>> also much rather err on the side of "overly paranoid" than "if it
>> works,
>> just let it in".  If someone ends up having to support both
>> windows SSPI
>> clients and unix Kerberos/GSSAPI clients it's entirely possible to
>> suggest they just make it POSTGRES and configure the clients
>> accordingly.
>
> Yeah, that's how we do it today with Kerberos. But it *would* be handy
> if this was easier ;-)

Don't read too much into the mod_auth_kerb situation.  The main
reason it still takes a specific, configured service name is that
neither Russ Allbery nor I has gotten around to submitting a proper
patch to fix that.  The only reason it was written the way it is in
the first place is that the ability to use GSS_C_NO_CREDENTIAL that
way is "obscure" and most people don't know it.  I can say that with
some confidence because Russ and I had a long discussion with Sam
Hartman about how it ought to be done.

I'm told that the way Apple's equivalent to mod_auth_kerb works is it
uses GSS_C_NO_CREDENTIAL and then does a case-insensitive compare of
the resulting match to "HTTP".  We could do the same thing, if you
think it's worth it.

------------------------------------------------------------------------
The opinions expressed in this message are mine,
not those of Caltech, JPL, NASA, or the US Government.
Henry.B.Hotz@jpl.nasa.gov, or hbhotz@oxy.edu



Re: Preliminary GSSAPI Patches

From
"Henry B. Hotz"
Date:
On Jun 24, 2007, at 11:03 PM, Magnus Hagander wrote:

>> I'm told that the way Apple's equivalent to mod_auth_kerb works is it
>> uses GSS_C_NO_CREDENTIAL and then does a case-insensitive compare of
>> the resulting match to "HTTP".  We could do the same thing, if you
>> think it's worth it.
>
> Do you know if this is documented somewhere? It's always nice with
> references.

Not as far as I know, publicly.

I heard most of it from an Apple developer at the 2005 WWDC (and I
inferred the rest from things Sam Hartman has said).  I guess that
technically puts it under NDA, except I think the code in question is
open source.  I don't know which project it's in so I haven't been
able to locate it to verify what I said for sure.

What I can say for certain concerns the client side.  Apple's Safari
browser went through at least two iterations before they got it
right:  1) in OSX 10.3 Safari would ask for a "server/
server.example.com" service ticket.  2) in early 10.4 Safari would
ask for a "http/server.example.com" service ticket (this actually
works fine if have Active Directory as your Kerberos server, and IIS,
or Apple as your web server).  3) in later 10.4 Safari asks for a
"HTTP/server.example.com" service ticket.  This is the correct thing
to do.

Due to the numbers of people talking to Apple about the situation
(state 2) during that WWDC, they publicly acknowledged the problem
and promised to fix it during the same WWDC.  If you have access to
the video recordings you can probably find the relevant session in
the latter half of the week.

The key technical point is that Kerberos is case sensitive, but
Windows Kerberos isn't.  We can deal with that how we choose, but I
kind of like Apple's solution.  It's annoying to have to put two
service principals in the keytab, but I personally prefer that to
going upper-case only just 'cause that's the only way Windows SSPI
clients can work with non-Windows servers.

------------------------------------------------------------------------
The opinions expressed in this message are mine,
not those of Caltech, JPL, NASA, or the US Government.
Henry.B.Hotz@jpl.nasa.gov, or hbhotz@oxy.edu



Re: Preliminary GSSAPI Patches

From
Magnus Hagander
Date:
On Mon, Jun 25, 2007 at 12:31:44AM -0700, Henry B. Hotz wrote:
>
> On Jun 24, 2007, at 11:03 PM, Magnus Hagander wrote:
>
> >>I'm told that the way Apple's equivalent to mod_auth_kerb works is it
> >>uses GSS_C_NO_CREDENTIAL and then does a case-insensitive compare of
> >>the resulting match to "HTTP".  We could do the same thing, if you
> >>think it's worth it.
> >
> >Do you know if this is documented somewhere? It's always nice with
> >references.
>
> Not as far as I know, publicly.
>
> I heard most of it from an Apple developer at the 2005 WWDC (and I
> inferred the rest from things Sam Hartman has said).  I guess that
> technically puts it under NDA, except I think the code in question is
> open source.  I don't know which project it's in so I haven't been
> able to locate it to verify what I said for sure.

Ok. no problem.


> What I can say for certain concerns the client side.  Apple's Safari
> browser went through at least two iterations before they got it
> right:  1) in OSX 10.3 Safari would ask for a "server/
> server.example.com" service ticket.  2) in early 10.4 Safari would
> ask for a "http/server.example.com" service ticket (this actually
> works fine if have Active Directory as your Kerberos server, and IIS,
> or Apple as your web server).  3) in later 10.4 Safari asks for a
> "HTTP/server.example.com" service ticket.  This is the correct thing
> to do.
>
> Due to the numbers of people talking to Apple about the situation
> (state 2) during that WWDC, they publicly acknowledged the problem
> and promised to fix it during the same WWDC.  If you have access to
> the video recordings you can probably find the relevant session in
> the latter half of the week.
>
> The key technical point is that Kerberos is case sensitive, but
> Windows Kerberos isn't.  We can deal with that how we choose, but I
> kind of like Apple's solution.  It's annoying to have to put two
> service principals in the keytab, but I personally prefer that to
> going upper-case only just 'cause that's the only way Windows SSPI
> clients can work with non-Windows servers.

Interesting, indeed. I think gonig down the same approach they were using
is the best way to do, so I've changed my working copy back to that
version, and will update the documentation with that information.

//Magnus


Re: Preliminary GSSAPI Patches

From
"Henry B. Hotz"
Date:
You know, I don't know what I was thinking when I sent this.  My
apologies for the late correction.

Anyone who has a copy of the "host" keys for a machine can
manufacture kerberos tickets for the "host" service on that machine
masquerading as absolutely anyone (including people who don't
exist).  Same for the "postgres" keys, and if the postgres server can
steal the host keys (or vice versa) then it's even worse.

I don't know of any exploit code that is designed for this purpose,
but there is code that uses this property to (legitimately) provide
kerberos tickets for AFS in scenarios where the KDC can't.

On Jun 24, 2007, at 10:10 PM, Henry B. Hotz wrote:

> On Jun 23, 2007, at 1:44 AM, 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.
>
> I don't have a problem with that, but you'll want multiple service
> names as soon as you want to support the SSPI.
>
> Also don't get too bent out of shape about some client using the
> wrong service name.  The client *still* needs to prove who it
> represents;  there's no hole there.  The only real security issue I
> can think of is that someone who subverts the PostgreSQL server
> could steal the "host" service keys and then (with a whole bunch of
> other work) masquerade as the SSH daemon.
>
>>>> If we do do that, then we need to allow for the ways Microsoft
>>>> mucks
>>>> with the case of the name.  (Kerberos is supposed to be case
>>>> sensitive, but Microsoft work that way.)  In particular I think we
>>>> may need both postgres/<server> and POSTGRES/<server> in the keytab
>>>> in order to support the to-be-written native Windows SSPI client at
>>>> the same time as the current Kerberos 5 and GSSAPI Unix clients.
>>>
>>> Supporting multiple, specific, keys might be an interesting
>>> challenge,
>>> but I'm not too keen on worrying about it right now regardless.  I'd
>>> also much rather err on the side of "overly paranoid" than "if it
>>> works,
>>> just let it in".  If someone ends up having to support both
>>> windows SSPI
>>> clients and unix Kerberos/GSSAPI clients it's entirely possible to
>>> suggest they just make it POSTGRES and configure the clients
>>> accordingly.
>>
>> Yeah, that's how we do it today with Kerberos. But it *would* be
>> handy
>> if this was easier ;-)
>
> Don't read too much into the mod_auth_kerb situation.  The main
> reason it still takes a specific, configured service name is that
> neither Russ Allbery nor I has gotten around to submitting a proper
> patch to fix that.  The only reason it was written the way it is in
> the first place is that the ability to use GSS_C_NO_CREDENTIAL that
> way is "obscure" and most people don't know it.  I can say that
> with some confidence because Russ and I had a long discussion with
> Sam Hartman about how it ought to be done.
>
> I'm told that the way Apple's equivalent to mod_auth_kerb works is
> it uses GSS_C_NO_CREDENTIAL and then does a case-insensitive
> compare of the resulting match to "HTTP".  We could do the same
> thing, if you think it's worth it.
>
> ----------------------------------------------------------------------
> --
> The opinions expressed in this message are mine,
> not those of Caltech, JPL, NASA, or the US Government.
> Henry.B.Hotz@jpl.nasa.gov, or hbhotz@oxy.edu
>
>


Re: Preliminary GSSAPI Patches

From
Tom Lane
Date:
"Henry B. Hotz" <hbhotz@oxy.edu> writes:
> You know, I don't know what I was thinking when I sent this.  My
> apologies for the late correction.
>
> Anyone who has a copy of the "host" keys for a machine can
> manufacture kerberos tickets for the "host" service on that machine
> masquerading as absolutely anyone (including people who don't
> exist).  Same for the "postgres" keys, and if the postgres server can
> steal the host keys (or vice versa) then it's even worse.
> [snip...]

Maybe I'm too dense, but I don't see a conclusion here.  Do we need to
change our code, our docs, both, or neither?

            regards, tom lane

Re: Preliminary GSSAPI Patches

From
Magnus Hagander
Date:
Tom Lane wrote:
> "Henry B. Hotz" <hbhotz@oxy.edu> writes:
>> You know, I don't know what I was thinking when I sent this.  My
>> apologies for the late correction.
>>
>> Anyone who has a copy of the "host" keys for a machine can
>> manufacture kerberos tickets for the "host" service on that machine
>> masquerading as absolutely anyone (including people who don't
>> exist).  Same for the "postgres" keys, and if the postgres server can
>> steal the host keys (or vice versa) then it's even worse.
>> [snip...]
>
> Maybe I'm too dense, but I don't see a conclusion here.  Do we need to
> change our code, our docs, both, or neither?

I don't think we do. If you use service keys per our documentation, you
should be fine. And if someone owns your host keys, you lost already.

//Magnus


Re: Preliminary GSSAPI Patches

From
"Henry B. Hotz"
Date:
I'm not suggesting any change.  Merely correcting a misstatement I
made earlier.

I believe the documentation already recommends best practice.

On Oct 10, 2007, at 10:53 AM, Magnus Hagander wrote:

> Tom Lane wrote:
>> "Henry B. Hotz" <hbhotz@oxy.edu> writes:
>>> You know, I don't know what I was thinking when I sent this.  My
>>> apologies for the late correction.
>>>
>>> Anyone who has a copy of the "host" keys for a machine can
>>> manufacture kerberos tickets for the "host" service on that machine
>>> masquerading as absolutely anyone (including people who don't
>>> exist).  Same for the "postgres" keys, and if the postgres server
>>> can
>>> steal the host keys (or vice versa) then it's even worse.
>>> [snip...]
>>
>> Maybe I'm too dense, but I don't see a conclusion here.  Do we
>> need to
>> change our code, our docs, both, or neither?
>
> I don't think we do. If you use service keys per our documentation,
> you
> should be fine. And if someone owns your host keys, you lost already.
>
> //Magnus

------------------------------------------------------------------------
The opinions expressed in this message are mine,
not those of Caltech, JPL, NASA, or the US Government.
Henry.B.Hotz@jpl.nasa.gov, or hbhotz@oxy.edu