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

Reply via email to