Обсуждение: Preliminary GSSAPI Patches
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
Вложения
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. +
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
Вложения
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
Вложения
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. +
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
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
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
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 === */
* 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
Вложения
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
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
* 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
Вложения
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
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 === */
> >>> 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
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
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
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
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 > >
"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
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
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