Magnus Hagander wrote:
> Stephen Frost wrote:
>> * Henry B. Hotz ([EMAIL PROTECTED]) 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, "[EMAIL PROTECTED]", 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, "[EMAIL PROTECTED]",
+ 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 === */
---------------------------(end of broadcast)---------------------------
TIP 7: You can help support the PostgreSQL project by donating at
http://www.postgresql.org/about/donate