From f70866fb2353dba162fc296f644e7ce77af6d79f Mon Sep 17 00:00:00 2001
From: Magnus Hagander <magnus@hagander.net>
Date: Mon, 23 Jul 2007 10:16:54 +0000
Subject: [PATCH] SSPI authentication on Windows. GSSAPI compatible client when
 doing Kerberos against a Unix server, and Windows-specific server-side
 authentication using SSPI "negotiate" method (Kerberos or NTLM).

Only builds properly with MSVC for now.
---
 doc/src/sgml/client-auth.sgml        |  40 +++-
 doc/src/sgml/libpq.sgml              |  22 ++-
 src/backend/libpq/auth.c             | 262 ++++++++++++++++++++++++-
 src/backend/libpq/hba.c              |   4 +-
 src/backend/libpq/pg_hba.conf.sample |   2 +-
 src/backend/libpq/pqcomm.c           |  10 +-
 src/backend/postmaster/postmaster.c  |  19 +-
 src/include/libpq/hba.h              |   5 +-
 src/include/libpq/libpq-be.h         |  26 ++-
 src/include/libpq/pqcomm.h           |   3 +-
 src/interfaces/libpq/fe-auth.c       | 277 +++++++++++++++++++++++++--
 src/interfaces/libpq/fe-connect.c    |  51 +++--
 src/interfaces/libpq/libpq-int.h     |  40 +++-
 src/tools/msvc/Mkvcbuild.pm          |   5 +-
 src/tools/msvc/Solution.pm           |  20 +-
 15 files changed, 708 insertions(+), 78 deletions(-)

diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index e3fa1c8b27d..dd3bd8be4d4 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/client-auth.sgml,v 1.99 2007/07/18 12:00:47 mha Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/client-auth.sgml,v 1.100 2007/07/23 10:16:53 mha Exp $ -->
 
 <chapter id="client-authentication">
  <title>Client Authentication</title>
@@ -358,6 +358,17 @@ hostnossl  <replaceable>database</replaceable>  <replaceable>user</replaceable>
         </listitem>
        </varlistentry>
 
+       <varlistentry>
+        <term><literal>sspi</></term>
+        <listitem>
+         <para>
+          Use SSPI to authenticate the user. This is only
+          available on Windows. See <xref
+          linkend="sspi-auth"> for details.
+         </para>
+        </listitem>
+       </varlistentry>
+
        <varlistentry>
         <term><literal>krb5</></term>
         <listitem>
@@ -677,6 +688,33 @@ local   db1,db2,@demodbs  all                         md5
    
   </sect2>
 
+  <sect2 id="sspi-auth">
+   <title>SSPI authentication</title>
+
+   <indexterm zone="sspi-auth">
+    <primary>SSPI</primary>
+   </indexterm>
+
+   <para>
+    <productname>SSPI</productname> is a <productname>Windows</productname>
+    technology for secure authentication with single sign-on. 
+    <productname>PostgreSQL</productname> will use SSPI in
+    <literal>negotiate</literal> mode, which will use
+    <productname>Kerberos</productname> when possible and automatically
+    fall back to <productname>NTLM</productname> in other cases.
+    <productname>SSPI</productname> authentication only works when both
+    server and client are running <productname>Windows</productname>.
+   </para>
+
+   <para>
+    When using <productname>Kerberos</productname> authentication, 
+    <productname>SSPI</productname> works the same way 
+    <productname>GSSAPI</productname> does. See <xref linkend="gssapi-auth">
+    for details.
+   </para>
+
+  </sect2>
+
   <sect2 id="kerberos-auth">
    <title>Kerberos authentication</title>
 
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index e1ee97ce182..117bc5f3509 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.238 2007/07/18 12:00:47 mha Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.239 2007/07/23 10:16:53 mha Exp $ -->
 
  <chapter id="libpq">
   <title><application>libpq</application> - C Library</title>
@@ -290,6 +290,17 @@ PGconn *PQconnectdb(const char *conninfo);
      </listitem>
     </varlistentry>
 
+    <varlistentry>
+     <term><literal>gsslib</literal></term>
+     <listitem>
+      <para>
+       GSS library to use for GSSAPI authentication. Only used on Windows.
+       Set to <literal>gssapi</literal> to force libpq to use the GSSAPI
+       library for authentication instead of the default SSPI.
+      </para>
+     </listitem>
+    </varlistentry>
+
     <varlistentry>
      <term><literal>service</literal></term>
      <listitem>
@@ -4220,6 +4231,15 @@ authenticating with Kerberos 5 or GSSAPI.
 </listitem>
 <listitem>
 <para>
+<indexterm>
+ <primary><envar>PGGSSLIB</envar></primary>
+</indexterm>
+<envar>PGGSSLIB</envar> sets the GSS library to use for GSSAPI 
+authentication. 
+</para>
+</listitem>
+<listitem>
+<para>
 <indexterm>
  <primary><envar>PGCONNECT_TIMEOUT</envar></primary>
 </indexterm>
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 224115fde8e..c475c6429d6 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.153 2007/07/12 20:36:11 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.154 2007/07/23 10:16:53 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -464,10 +464,14 @@ pg_GSS_recvauth(Port *port)
 			/*
 			 * Negotiation generated data to be sent to the client.
 			 */
+			OM_uint32	lmin_s;
+
 			elog(DEBUG4, "sending GSS response token of length %u",
 				 (unsigned int) port->gss->outbuf.length);
 
 			sendAuthRequest(port, AUTH_REQ_GSS_CONT);
+
+			gss_release_buffer(&lmin_s, &port->gss->outbuf);
 		}
 
 		if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
@@ -536,7 +540,7 @@ pg_GSS_recvauth(Port *port)
 	return STATUS_OK;
 }
 
-#else	/* no ENABLE_GSS */
+#else /* no ENABLE_GSS */
 static int
 pg_GSS_recvauth(Port *port)
 {
@@ -547,6 +551,245 @@ pg_GSS_recvauth(Port *port)
 }
 #endif	/* ENABLE_GSS */
 
+#ifdef ENABLE_SSPI
+static void
+pg_SSPI_error(int severity, char *errmsg, SECURITY_STATUS r)
+{
+	char sysmsg[256];
+
+	if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, r, 0, sysmsg, sizeof(sysmsg), NULL) == 0)
+		ereport(severity,
+		        (errmsg_internal("%s", errmsg),
+				errdetail("sspi error %x", r)));
+	else
+		ereport(severity,
+		        (errmsg_internal("%s", errmsg),
+				errdetail("%s (%x)", sysmsg, r)));
+}
+
+
+static int
+pg_SSPI_recvauth(Port *port)
+{
+	int				mtype;
+	StringInfoData	buf;
+	SECURITY_STATUS r;
+	CredHandle		sspicred;
+	CtxtHandle		*sspictx = NULL,
+		            newctx;
+	TimeStamp		expiry;
+	ULONG			contextattr;
+	SecBufferDesc	inbuf;
+	SecBufferDesc	outbuf;
+	SecBuffer		OutBuffers[1];
+	SecBuffer		InBuffers[1];
+	HANDLE			token;
+	TOKEN_USER		*tokenuser;
+	DWORD			retlen;
+	char			accountname[MAXPGPATH];
+	char			domainname[MAXPGPATH];
+	DWORD			accountnamesize = sizeof(accountname);
+	DWORD			domainnamesize = sizeof(domainname);
+	SID_NAME_USE	accountnameuse;
+
+
+	/*
+	 * Acquire a handle to the server credentials.
+	 */
+	r = AcquireCredentialsHandle(NULL,
+		"negotiate",
+		SECPKG_CRED_INBOUND,
+		NULL,
+		NULL,
+		NULL,
+		NULL,
+		&sspicred,
+		&expiry);
+	if (r != SEC_E_OK)
+		pg_SSPI_error(ERROR, 
+					gettext_noop("could not acquire SSPI credentials handle"), r);
+
+	/*
+	 * Loop through SSPI 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 SSPI response, got message type %d",
+							 mtype)));
+			return STATUS_ERROR;
+		}
+
+		/* Get the actual SSPI token */
+		initStringInfo(&buf);
+		if (pq_getmessage(&buf, 2000))
+		{
+			/* EOF - pq_getmessage already logged error */
+			pfree(buf.data);
+			return STATUS_ERROR;
+		}
+
+		/* Map to SSPI style buffer */
+		inbuf.ulVersion = SECBUFFER_VERSION;
+		inbuf.cBuffers = 1;
+		inbuf.pBuffers = InBuffers;
+		InBuffers[0].pvBuffer = buf.data;
+		InBuffers[0].cbBuffer = buf.len;
+		InBuffers[0].BufferType = SECBUFFER_TOKEN;
+
+		/* Prepare output buffer */
+		OutBuffers[0].pvBuffer = NULL;
+		OutBuffers[0].BufferType = SECBUFFER_TOKEN;
+		OutBuffers[0].cbBuffer = 0;
+		outbuf.cBuffers = 1;
+		outbuf.pBuffers = OutBuffers;
+		outbuf.ulVersion = SECBUFFER_VERSION;
+
+
+		elog(DEBUG4, "Processing received SSPI token of length %u", 
+			 (unsigned int) buf.len);
+
+		r = AcceptSecurityContext(&sspicred,
+			sspictx,
+			&inbuf,
+			ASC_REQ_ALLOCATE_MEMORY,
+			SECURITY_NETWORK_DREP,
+			&newctx,
+			&outbuf,
+			&contextattr,
+			NULL);
+
+		/* input buffer no longer used */
+		pfree(buf.data);
+
+		if (outbuf.cBuffers > 0 && outbuf.pBuffers[0].cbBuffer > 0)
+		{
+			/*
+			 * Negotiation generated data to be sent to the client.
+			 */
+			elog(DEBUG4, "sending SSPI response token of length %u",
+				 (unsigned int) outbuf.pBuffers[0].cbBuffer);
+
+			port->gss->outbuf.length = outbuf.pBuffers[0].cbBuffer;
+			port->gss->outbuf.value = outbuf.pBuffers[0].pvBuffer;
+
+			sendAuthRequest(port, AUTH_REQ_GSS_CONT);
+
+			FreeContextBuffer(outbuf.pBuffers[0].pvBuffer);
+		}
+
+		if (r != SEC_E_OK && r != SEC_I_CONTINUE_NEEDED)
+		{
+			if (sspictx != NULL)
+			{
+				DeleteSecurityContext(sspictx);
+				free(sspictx);
+			}
+			FreeCredentialsHandle(&sspicred);
+			pg_SSPI_error(ERROR, 
+					gettext_noop("could not accept SSPI security context"), r);
+		}
+
+		if (sspictx == NULL)
+		{
+			sspictx = malloc(sizeof(CtxtHandle));
+			if (sspictx == NULL)
+				ereport(ERROR,
+					(errmsg("out of memory")));
+
+			memcpy(sspictx, &newctx, sizeof(CtxtHandle));
+		}
+
+		if (r == SEC_I_CONTINUE_NEEDED)
+			elog(DEBUG4, "SSPI continue needed");
+
+	} while (r == SEC_I_CONTINUE_NEEDED);
+
+
+	/*
+	 * Release service principal credentials
+	 */
+	FreeCredentialsHandle(&sspicred);
+
+
+	/*
+	 * SEC_E_OK 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.
+	 */
+
+	r = QuerySecurityContextToken(sspictx, &token);
+	if (r != SEC_E_OK)
+		pg_SSPI_error(ERROR,
+			gettext_noop("could not get security token from context"), r);
+
+	/*
+	 * No longer need the security context, everything from here on uses the
+	 * token instead.
+	 */
+	DeleteSecurityContext(sspictx);
+	free(sspictx);
+
+	if (!GetTokenInformation(token, TokenUser, NULL, 0, &retlen) && GetLastError() != 122)
+		ereport(ERROR,
+				(errmsg_internal("could not get token user size: error code %d",
+					(int) GetLastError())));
+
+	tokenuser = malloc(retlen);
+	if (tokenuser == NULL)
+		ereport(ERROR,
+				(errmsg("out of memory")));
+
+	if (!GetTokenInformation(token, TokenUser, tokenuser, retlen, &retlen))
+		ereport(ERROR,
+				(errmsg_internal("could not get user token: error code %d",
+					(int) GetLastError())));
+
+	if (!LookupAccountSid(NULL, tokenuser->User.Sid, accountname, &accountnamesize, 
+							domainname, &domainnamesize, &accountnameuse))
+		ereport(ERROR,
+				(errmsg_internal("could not lookup acconut sid: error code %d",
+					(int) GetLastError())));
+
+	free(tokenuser);
+
+	/*
+	 * We have the username (without domain/realm) in accountname, compare 
+	 * to the supplied value. In SSPI, always compare case insensitive.
+	 */
+	if (pg_strcasecmp(port->user_name, accountname))
+	{
+		/* GSS name and PGUSER are not equivalent */
+		elog(DEBUG2, 
+			 "provided username (%s) and SSPI username (%s) don't match",
+			 port->user_name, accountname);
+
+		return STATUS_ERROR;
+	}
+	
+	return STATUS_OK;
+}
+#else	/* no ENABLE_SSPI */
+static int
+pg_SSPI_recvauth(Port *port)
+{
+	ereport(LOG,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("SSPI not implemented on this server.")));
+	return STATUS_ERROR;
+}
+#endif	/* ENABLE_SSPI */
+
 
 /*
  * Tell the user the authentication failed, but not (much about) why.
@@ -589,6 +832,9 @@ auth_failed(Port *port, int status)
 		case uaGSS:
 			errstr = gettext_noop("GSSAPI authentication failed for user \"%s\"");
 			break;
+		case uaSSPI:
+			errstr = gettext_noop("SSPI authentication failed for user \"%s\"");
+			break;
 		case uaTrust:
 			errstr = gettext_noop("\"trust\" authentication failed for user \"%s\"");
 			break;
@@ -689,6 +935,11 @@ ClientAuthentication(Port *port)
 			status = pg_GSS_recvauth(port);
 			break;
 
+		case uaSSPI:
+			sendAuthRequest(port, AUTH_REQ_SSPI);
+			status = pg_SSPI_recvauth(port);
+			break;
+
 		case uaIdent:
 
 			/*
@@ -778,20 +1029,17 @@ sendAuthRequest(Port *port, AuthRequest areq)
 	else if (areq == AUTH_REQ_CRYPT)
 		pq_sendbytes(&buf, port->cryptSalt, 2);
 
-#ifdef ENABLE_GSS
+#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
 	/* Add the authentication data for the next step of
-	 * the GSSAPI negotiation. */
+	 * the GSSAPI or SSPI negotiation. */
 	else if (areq == AUTH_REQ_GSS_CONT)
 	{
 		if (port->gss->outbuf.length > 0)
 		{
-			OM_uint32	lmin_s;
-
 			elog(DEBUG4, "sending GSS token of length %u",
 				 (unsigned int) port->gss->outbuf.length);
 
 			pq_sendbytes(&buf, port->gss->outbuf.value, port->gss->outbuf.length);
-			gss_release_buffer(&lmin_s, &port->gss->outbuf);
 		}
 	}
 #endif
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 9f917b16661..c3cde8cb1bf 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.161 2007/07/10 13:14:20 mha Exp $
+ *	  $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.162 2007/07/23 10:16:53 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -604,6 +604,8 @@ parse_hba_auth(ListCell **line_item, UserAuth *userauth_p,
 		*userauth_p = uaKrb5;
 	else if (strcmp(token, "gss") == 0)
 		*userauth_p = uaGSS;
+	else if (strcmp(token, "sspi") == 0)
+		*userauth_p = uaSSPI;
 	else if (strcmp(token, "reject") == 0)
 		*userauth_p = uaReject;
 	else if (strcmp(token, "md5") == 0)
diff --git a/src/backend/libpq/pg_hba.conf.sample b/src/backend/libpq/pg_hba.conf.sample
index ebdf76904ae..1447a8c4bef 100644
--- a/src/backend/libpq/pg_hba.conf.sample
+++ b/src/backend/libpq/pg_hba.conf.sample
@@ -34,7 +34,7 @@
 # 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",
+# METHOD can be "trust", "reject", "md5", "crypt", "password", "gss", "sspi",
 # "krb5", "ident", "pam" or "ldap".  Note that "password" sends passwords
 # in clear text; "md5" is preferred since it sends encrypted passwords.
 #
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index a40e6a6cb27..f5dccfda7ab 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -30,7 +30,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	$PostgreSQL: pgsql/src/backend/libpq/pqcomm.c,v 1.193 2007/07/10 13:14:20 mha Exp $
+ *	$PostgreSQL: pgsql/src/backend/libpq/pqcomm.c,v 1.194 2007/07/23 10:16:54 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -173,15 +173,21 @@ pq_close(int code, Datum arg)
 {
 	if (MyProcPort != NULL)
 	{
+#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
 #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
+#endif /* ENABLE_GSS */
+		/* GSS and SSPI share the port->gss struct */
+
+		free(MyProcPort->gss);
+#endif /* ENABLE_GSS || ENABLE_SSPI */
 
 		/* Cleanly shut down SSL layer */
 		secure_close(MyProcPort);
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 240f150d62a..7a1270b0149 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -37,7 +37,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.533 2007/07/19 19:13:43 adunstan Exp $
+ *	  $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.534 2007/07/23 10:16:54 mha Exp $
  *
  * NOTES
  *
@@ -1733,7 +1733,8 @@ ConnCreate(int serverFd)
 	/*
      * Allocate GSSAPI specific state struct
 	 */
-#ifdef ENABLE_GSS
+#ifndef EXEC_BACKEND
+#if defined(ENABLE_GSS) || defined(ENABLE_SSPI) 
 	port->gss = (pg_gssinfo *)calloc(1, sizeof(pg_gssinfo));
 	if (!port->gss)
 	{
@@ -1742,6 +1743,7 @@ ConnCreate(int serverFd)
 				 errmsg("out of memory")));
 		ExitPostmaster(1);
 	}
+#endif
 #endif
 
 	return port;
@@ -3344,6 +3346,19 @@ SubPostmasterMain(int argc, char *argv[])
 	memset(&port, 0, sizeof(Port));
 	read_backend_variables(argv[2], &port);
 
+	/* 
+	 * Set up memory area for GSS information. Mirrors the code in
+	 * ConnCreate for the non-exec case.
+	 */
+#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
+	port.gss = (pg_gssinfo *)calloc(1, sizeof(pg_gssinfo));
+	if (!port.gss)
+		ereport(FATAL,
+				(errcode(ERRCODE_OUT_OF_MEMORY),
+				 errmsg("out of memory")));
+#endif
+
+
 	/* Check we got appropriate args */
 	if (argc < 3)
 		elog(FATAL, "invalid subpostmaster invocation");
diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h
index b68aa5899b5..603d8635238 100644
--- a/src/include/libpq/hba.h
+++ b/src/include/libpq/hba.h
@@ -4,7 +4,7 @@
  *	  Interface to hba.c
  *
  *
- * $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.46 2007/07/10 13:14:21 mha Exp $
+ * $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.47 2007/07/23 10:16:54 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,7 +23,8 @@ typedef enum UserAuth
 	uaPassword,
 	uaCrypt,
 	uaMD5,
-	uaGSS
+	uaGSS,
+	uaSSPI
 #ifdef USE_PAM
 	,uaPAM
 #endif   /* USE_PAM */
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 891ab9614c0..319e5e86100 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/libpq/libpq-be.h,v 1.61 2007/07/12 14:43:21 mha Exp $
+ * $PostgreSQL: pgsql/src/include/libpq/libpq-be.h,v 1.62 2007/07/23 10:16:54 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -45,6 +45,22 @@
 #endif
 #endif /* ENABLE_GSS */
 
+#ifdef ENABLE_SSPI
+#define SECURITY_WIN32
+#include <security.h>
+#undef SECURITY_WIN32
+
+#ifndef ENABLE_GSS
+/*
+ * Define a fake structure compatible with GSSAPI on Unix.
+ */
+typedef struct {
+	void *value;
+	int length;
+} gss_buffer_desc;
+#endif
+#endif /* ENABLE_SSPI */
+
 #include "libpq/hba.h"
 #include "libpq/pqcomm.h"
 #include "utils/timestamp.h"
@@ -59,13 +75,15 @@ typedef enum CAC_state
 /*
  * GSSAPI specific state information
  */
-#ifdef ENABLE_GSS
+#if defined(ENABLE_GSS) | defined(ENABLE_SSPI)
 typedef struct
 {
+	gss_buffer_desc	outbuf;		/* GSSAPI output token buffer */
+#ifdef ENABLE_GSS
 	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 */
+#endif
 } pg_gssinfo;
 #endif
 
@@ -128,7 +146,7 @@ typedef struct Port
 	int			keepalives_interval;
 	int			keepalives_count;
 
-#ifdef ENABLE_GSS
+#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
 	/*
 	 * If GSSAPI is supported, store GSSAPI information.
 	 * Oterwise, store a NULL pointer to make sure offsets
diff --git a/src/include/libpq/pqcomm.h b/src/include/libpq/pqcomm.h
index 8e66aaa9685..ec7427b6de0 100644
--- a/src/include/libpq/pqcomm.h
+++ b/src/include/libpq/pqcomm.h
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/libpq/pqcomm.h,v 1.105 2007/07/10 13:14:21 mha Exp $
+ * $PostgreSQL: pgsql/src/include/libpq/pqcomm.h,v 1.106 2007/07/23 10:16:54 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -158,6 +158,7 @@ extern bool Db_user_namespace;
 #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 */
+#define AUTH_REQ_SSPI		9   /* SSPI negotiate without wrap() */
 
 typedef uint32 AuthRequest;
 
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index 4c3207f63a6..e08dae8125d 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -10,7 +10,7 @@
  * exceed INITIAL_EXPBUFFER_SIZE (currently 256 bytes).
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-auth.c,v 1.127 2007/07/12 14:43:21 mha Exp $
+ *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-auth.c,v 1.128 2007/07/23 10:16:54 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -329,11 +329,6 @@ pg_krb5_sendauth(char *PQerrormsg, int sock, const char *hostname, const char *s
 /*
  * GSSAPI authentication system.
  */
-#if defined(HAVE_GSSAPI_H)
-#include <gssapi.h>
-#else
-#include <gssapi/gssapi.h>
-#endif
 
 #if defined(WIN32) && !defined(WIN32_ONLY_COMPILER)
 /*
@@ -378,7 +373,7 @@ pg_GSS_error_int(char *mprefix, char *msg, int msglen,
  * GSSAPI errors contains two parts. Put as much as possible of
  * both parts into the string.
  */
-void
+static void
 pg_GSS_error(char *mprefix, char *msg, int msglen,
 	OM_uint32 maj_stat, OM_uint32 min_stat)
 {
@@ -407,7 +402,7 @@ pg_GSS_continue(char *PQerrormsg, PGconn *conn)
 			&conn->gctx,
 			conn->gtarg_nam,
 			GSS_C_NO_OID,
-			conn->gflags,
+			GSS_C_MUTUAL_FLAG,
 			0,
 			GSS_C_NO_CHANNEL_BINDINGS,
 			(conn->gctx==GSS_C_NO_CONTEXT)?GSS_C_NO_BUFFER:&conn->ginbuf,
@@ -504,7 +499,192 @@ pg_GSS_startup(char *PQerrormsg, PGconn *conn)
 
 	return pg_GSS_continue(PQerrormsg, conn);
 }
-#endif
+#endif /* ENABLE_GSS */
+
+
+#ifdef ENABLE_SSPI
+/*
+ * SSPI authentication system (Windows only)
+ */
+
+static void
+pg_SSPI_error(char *mprefix, char *msg, int msglen, SECURITY_STATUS r)
+{
+	char sysmsg[256];
+
+	if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, r, 0, sysmsg, sizeof(sysmsg), NULL) == 0)
+		snprintf(msg, msglen, "%s: sspi error %x", mprefix, r);
+	else
+		snprintf(msg, msglen, "%s: %s (%x)", mprefix, sysmsg, r);
+}
+
+/* 
+ * Continue SSPI authentication with next token as needed.
+ */
+static int
+pg_SSPI_continue(char *PQerrormsg, PGconn *conn)
+{
+	SECURITY_STATUS	r;
+	CtxtHandle		newContext;
+	ULONG			contextAttr;
+	SecBufferDesc	inbuf;
+	SecBufferDesc	outbuf;
+	SecBuffer		OutBuffers[1];
+	SecBuffer		InBuffers[1];
+
+	if (conn->sspictx != NULL)
+	{
+		/*
+		 * On runs other than the first we have some data to send. Put this
+		 * data in a SecBuffer type structure.
+		 */
+		inbuf.ulVersion = SECBUFFER_VERSION;
+		inbuf.cBuffers = 1;
+		inbuf.pBuffers = InBuffers;
+		InBuffers[0].pvBuffer = conn->ginbuf.value;
+		InBuffers[0].cbBuffer = conn->ginbuf.length;
+		InBuffers[0].BufferType = SECBUFFER_TOKEN;
+	}
+
+	OutBuffers[0].pvBuffer = NULL;
+	OutBuffers[0].BufferType = SECBUFFER_TOKEN;
+	OutBuffers[0].cbBuffer = 0;
+	outbuf.cBuffers = 1;
+	outbuf.pBuffers = OutBuffers;
+	outbuf.ulVersion = SECBUFFER_VERSION;
+
+	r = InitializeSecurityContext(conn->sspicred,
+		conn->sspictx,
+		conn->sspitarget,
+		ISC_REQ_ALLOCATE_MEMORY,
+		0,
+		SECURITY_NETWORK_DREP,
+		(conn->sspictx == NULL)?NULL:&inbuf,
+		0,
+		&newContext,
+		&outbuf,
+		&contextAttr,
+		NULL);
+	
+	if (r != SEC_E_OK && r != SEC_I_CONTINUE_NEEDED)
+	{
+		pg_SSPI_error(libpq_gettext("SSPI continuation error"),
+			PQerrormsg, PQERRORMSG_LENGTH, r);
+
+		return STATUS_ERROR;
+	}
+
+	if (conn->sspictx == NULL)
+	{
+		/* On first run, transfer retreived context handle */
+		conn->sspictx = malloc(sizeof(CtxtHandle));
+		if (conn->sspictx == NULL)
+		{
+			strncpy(PQerrormsg, libpq_gettext("out of memory\n"), PQERRORMSG_LENGTH);
+			return STATUS_ERROR;
+		}
+		memcpy(conn->sspictx, &newContext, sizeof(CtxtHandle));
+	}
+	else
+	{
+		/*
+		 * On subsequent runs when we had data to send, free buffers that contained
+		 * this data.
+		 */
+		free(conn->ginbuf.value);
+		conn->ginbuf.value = NULL;
+		conn->ginbuf.length = 0;
+	}
+
+	/*
+	 * If SSPI returned any data to be sent to the server (as it normally would),
+	 * send this data as a password packet.
+	 */
+	if (outbuf.cBuffers > 0)
+	{
+		if (outbuf.cBuffers != 1)
+		{
+			/*
+			 * This should never happen, at least not for Kerberos authentication. Keep check
+			 * in case it shows up with other authentication methods later.
+			 */
+			strncpy(PQerrormsg, "SSPI returned invalid number of output buffers\n", PQERRORMSG_LENGTH);
+			return STATUS_ERROR;
+		}
+
+		if (pqPacketSend(conn, 'p',
+			outbuf.pBuffers[0].pvBuffer, outbuf.pBuffers[0].cbBuffer))
+		{
+			FreeContextBuffer(outbuf.pBuffers[0].pvBuffer);
+			return STATUS_ERROR;
+		}
+		FreeContextBuffer(outbuf.pBuffers[0].pvBuffer);
+	}
+
+	/* Cleanup is handled by the code in freePGconn() */
+	return STATUS_OK;
+}
+
+/* 
+ * Send initial SSPI authentication token.
+ * If use_negotiate is 0, use kerberos authentication package which is
+ * compatible with Unix. If use_negotiate is 1, use the negotiate package
+ * which supports both kerberos and NTLM, but is not compatible with Unix.
+ */
+static int
+pg_SSPI_startup(char *PQerrormsg, PGconn *conn, int use_negotiate)
+{
+	SECURITY_STATUS	r;
+	TimeStamp		expire;
+
+	conn->sspictx = NULL;
+
+	/*
+	 * Retreive credentials handle
+	 */
+	conn->sspicred = malloc(sizeof(CredHandle));
+	if (conn->sspicred == NULL)
+	{
+		strncpy(PQerrormsg, libpq_gettext("out of memory\n"), PQERRORMSG_LENGTH);
+		return STATUS_ERROR;
+	}
+
+	r = AcquireCredentialsHandle(NULL, use_negotiate?"negotiate":"kerberos", SECPKG_CRED_OUTBOUND, NULL, NULL, NULL, NULL, conn->sspicred, &expire);
+	if (r != SEC_E_OK)
+	{
+		pg_SSPI_error("acquire credentials failed", PQerrormsg, PQERRORMSG_LENGTH, r);
+		free(conn->sspicred);
+		conn->sspicred = NULL;
+		return STATUS_ERROR;
+	}
+
+	/*
+	 * Compute target principal name. SSPI has a different format from GSSAPI, but
+	 * not more complex. We can skip the @REALM part, because Windows will fill that
+	 * in for us automatically.
+	 */
+	if (conn->pghost == NULL)
+	{
+		strncpy(PQerrormsg, libpq_gettext("hostname must be specified\n"), PQERRORMSG_LENGTH);
+		return STATUS_ERROR;
+	}
+	conn->sspitarget = malloc(strlen(conn->krbsrvname)+strlen(conn->pghost)+2);
+	if (!conn->sspitarget)
+	{
+		strncpy(PQerrormsg, libpq_gettext("out of memory\n"), PQERRORMSG_LENGTH);
+		return STATUS_ERROR;
+	}
+	sprintf(conn->sspitarget, "%s/%s", conn->krbsrvname, conn->pghost);
+
+	/*
+	 * Indicate that we're in SSPI authentication mode to make sure that
+	 * pg_SSPI_continue is called next time in the negotiation.
+	 */
+	conn->usesspi = 1;
+
+	return pg_SSPI_continue(PQerrormsg, conn);
+}
+#endif /* ENABLE_SSPI */
 
 /*
  * Respond to AUTH_REQ_SCM_CREDS challenge.
@@ -671,27 +851,60 @@ pg_fe_sendauth(AuthRequest areq, PGconn *conn, const char *hostname,
 			return STATUS_ERROR;
 #endif
 
-#ifdef ENABLE_GSS
+#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
 		case AUTH_REQ_GSS:
-			pglock_thread();
-			if (pg_GSS_startup(PQerrormsg, conn) != STATUS_OK)
 			{
-				/* PQerrormsg already filled in. */
+				int r;
+				pglock_thread();
+				/*
+				 * If we have both GSS and SSPI support compiled in, use SSPI
+				 * support by default. This is overridable by a connection string parameter.
+				 * Note that when using SSPI we still leave the negotiate parameter off,
+				 * since we want SSPI to use the GSSAPI kerberos protocol. For actual
+				 * SSPI negotiate protocol, we use AUTH_REQ_SSPI.
+				 */
+#if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
+				if (conn->gsslib && (pg_strcasecmp(conn->gsslib, "gssapi") == 0))
+					r = pg_GSS_startup(PQerrormsg, conn);
+				else
+					r = pg_SSPI_startup(PQerrormsg, conn, 0);
+#elif defined(ENABLE_GSS) && !defined(ENABLE_SSPI)
+				r = pg_GSS_startup(PQerrormsg, conn);
+#elif !defined(ENABLE_GSS) && defined(ENABLE_SSPI)
+				r = pg_SSPI_startup(PQerrormsg, conn, 0);
+#endif
+				if (r != STATUS_OK)
+				{
+					/* PQerrormsg already filled in. */
+					pgunlock_thread();
+					return STATUS_ERROR;
+				}
 				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. */
+				int r;
+				pglock_thread();
+#if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
+				if (conn->usesspi)
+					r = pg_SSPI_continue(PQerrormsg, conn);
+				else
+					r = pg_GSS_continue(PQerrormsg, conn);
+#elif defined(ENABLE_GSS) && !defined(ENABLE_SSPI)
+				r = pg_GSS_continue(PQerrormsg, conn);
+#elif !defined(ENABLE_GSS) && defined(ENABLE_SSPI)
+				r = pg_SSPI_continue(PQerrormsg, conn);
+#endif
+				if (r != STATUS_OK)
+				{
+					/* PQerrormsg already filled in. */
+					pgunlock_thread();
+					return STATUS_ERROR;
+				}
 				pgunlock_thread();
-				return STATUS_ERROR;
 			}
-			pgunlock_thread();
 			break;
 
 #else
@@ -702,6 +915,30 @@ pg_fe_sendauth(AuthRequest areq, PGconn *conn, const char *hostname,
 			return STATUS_ERROR;
 #endif
 
+#ifdef ENABLE_SSPI
+		case AUTH_REQ_SSPI:
+			/* 
+			 * SSPI has it's own startup message so libpq can decide which
+			 * method to use. Indicate to pg_SSPI_startup that we want
+			 * SSPI negotiation instead of Kerberos.
+			 */
+			pglock_thread();
+			if (pg_SSPI_startup(PQerrormsg, conn, 1) != STATUS_OK)
+			{
+				/* PQerrormsg already filled in. */
+				pgunlock_thread();
+				return STATUS_ERROR;
+			}
+			pgunlock_thread();
+			break;
+#else
+		case AUTH_REQ_SSPI:
+			snpritnf(PQerrormsg, PQERRORMSG_LENGTH,
+					libpq_gettext("SSPI authentication not supported\n"));
+			return STATUS_ERROR;
+#endif
+
+
 		case AUTH_REQ_MD5:
 		case AUTH_REQ_CRYPT:
 		case AUTH_REQ_PASSWORD:
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index aebce1caf6e..f3eafb1eeb4 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.349 2007/07/11 08:27:33 mha Exp $
+ *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.350 2007/07/23 10:16:54 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -181,12 +181,18 @@ static const PQconninfoOption PQconninfoOptions[] = {
 	{"sslmode", "PGSSLMODE", DefaultSSLMode, NULL,
 	"SSL-Mode", "", 8},			/* sizeof("disable") == 8 */
 
-#if defined(KRB5) || defined(ENABLE_GSS)
+#if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI)
 	/* Kerberos and GSSAPI authentication support specifying the service name */
 	{"krbsrvname", "PGKRBSRVNAME", PG_KRB_SRVNAM, NULL,
 	"Kerberos-service-name", "", 20},
 #endif
 
+#if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
+	/* GSSAPI and SSPI both enabled, give a way to override which is used by default */
+	{"gsslib", "PGGSSLIB", NULL, NULL,
+	"GSS-library", "", 7},		/* sizeof("gssapi") = 7 */
+#endif
+
 	/* Terminating entry --- MUST BE LAST */
 	{NULL, NULL, NULL, NULL,
 	NULL, NULL, 0}
@@ -412,10 +418,14 @@ connectOptions1(PGconn *conn, const char *conninfo)
 		conn->sslmode = strdup("require");
 	}
 #endif
-#if defined(KRB5) || defined(ENABLE_GSS)
+#if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI)
 	tmp = conninfo_getval(connOptions, "krbsrvname");
 	conn->krbsrvname = tmp ? strdup(tmp) : NULL;
 #endif
+#if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
+	tmp = conninfo_getval(connOptions, "gsslib");
+	conn->gsslib = tmp ? strdup(tmp) : NULL;
+#endif
 
 	/*
 	 * Free the option info - all is in conn now
@@ -1661,22 +1671,13 @@ keep_going:						/* We will come back to here until there is
 						return PGRES_POLLING_READING;
 					}
 				}
-#ifdef ENABLE_GSS
+#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
 				/*
-				 * 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
+				 * Continue GSSAPI/SSPI authentication
 				 */
 				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 - 
@@ -2002,7 +2003,7 @@ freePGconn(PGconn *conn)
 		free(conn->pgpass);
 	if (conn->sslmode)
 		free(conn->sslmode);
-#if defined(KRB5) || defined(ENABLE_GSS)
+#if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI)
 	if (conn->krbsrvname)
 		free(conn->krbsrvname);
 #endif
@@ -2030,6 +2031,26 @@ freePGconn(PGconn *conn)
 		if (conn->goutbuf.length)
 			gss_release_buffer(&min_s, &conn->goutbuf);
 	}
+#endif
+#ifdef ENABLE_SSPI
+	{
+		if (conn->ginbuf.length)
+			free(conn->ginbuf.value);
+
+		if (conn->sspitarget)
+			free(conn->sspitarget);
+
+		if (conn->sspicred)
+		{
+			FreeCredentialsHandle(conn->sspicred);
+			free(conn->sspicred);
+		}
+		if (conn->sspictx)
+		{
+			DeleteSecurityContext(conn->sspictx);
+			free(conn->sspictx);
+		}
+	}
 #endif
 	pstatus = conn->pstatus;
 	while (pstatus != NULL)
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index b497e1d68d1..cc3fa0106d2 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -12,7 +12,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.123 2007/07/12 14:36:52 mha Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.124 2007/07/23 10:16:54 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -52,6 +52,22 @@
 #endif
 #endif
 
+#ifdef ENABLE_SSPI
+#define SECURITY_WIN32
+#include <security.h>
+#undef SECURITY_WIN32
+
+#ifndef ENABLE_GSS
+/*
+ * Define a fake structure compatible with GSSAPI on Unix.
+ */
+typedef struct {
+	void *value;
+	int length;
+} gss_buffer_desc;
+#endif
+#endif /* ENABLE_SSPI */
+
 #ifdef USE_SSL
 #include <openssl/ssl.h>
 #include <openssl/err.h>
@@ -276,7 +292,7 @@ struct pg_conn
 	char	   *pguser;			/* Postgres username and password, if any */
 	char	   *pgpass;
 	char	   *sslmode;		/* SSL mode (require,prefer,allow,disable) */
-#if defined(KRB5) || defined(ENABLE_GSS)
+#if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI)
 	char	   *krbsrvname;		/* Kerberos service name */
 #endif
 
@@ -361,11 +377,23 @@ struct pg_conn
 #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
 
+#ifdef ENABLE_SSPI
+#ifndef ENABLE_GSS
+	gss_buffer_desc	ginbuf;		/* GSS input token */
+#else
+	char			*gsslib;	/* What GSS librart to use ("gssapi" or "sspi") */
+#endif
+	CredHandle		*sspicred;	/* SSPI credentials handle */
+	CtxtHandle		*sspictx;	/* SSPI context */
+	char			*sspitarget;/* SSPI target name */
+	int				usesspi;	/* Indicate if SSPI is in use on the connection */
+#endif
+
+
 	/* Buffer for current error message */
 	PQExpBufferData errorMessage;		/* expansible string */
 
@@ -415,12 +443,6 @@ extern pgthreadlock_t pg_g_threadlock;
 #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 === */
 
 extern void pqSetResultError(PGresult *res, const char *msg);
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 1bc19ed70fe..a1eb2af64b4 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -3,7 +3,7 @@ package Mkvcbuild;
 #
 # Package that generates build files for msvc build
 #
-# $PostgreSQL: pgsql/src/tools/msvc/Mkvcbuild.pm,v 1.14 2007/07/07 07:43:20 mha Exp $
+# $PostgreSQL: pgsql/src/tools/msvc/Mkvcbuild.pm,v 1.15 2007/07/23 10:16:54 mha Exp $
 #
 use Carp;
 use Win32;
@@ -66,7 +66,7 @@ sub mkvcbuild
     $postgres->AddFiles('src\backend\bootstrap','bootscanner.l','bootparse.y');
     $postgres->AddFiles('src\backend\utils\misc','guc-file.l');
     $postgres->AddDefine('BUILDING_DLL');
-    $postgres->AddLibrary('wsock32.lib ws2_32.lib');
+    $postgres->AddLibrary('wsock32.lib ws2_32.lib secur32.lib');
     $postgres->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
     $postgres->FullExportDLL('postgres.lib');
 
@@ -120,6 +120,7 @@ sub mkvcbuild
     $libpq->AddDefine('FRONTEND');
     $libpq->AddIncludeDir('src\port');
     $libpq->AddLibrary('wsock32.lib');
+    $libpq->AddLibrary('secur32.lib');
     $libpq->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
     $libpq->UseDef('src\interfaces\libpq\libpqdll.def');
     $libpq->ReplaceFile('src\interfaces\libpq\libpqrc.c','src\interfaces\libpq\libpq.rc');
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index 3a23a15431d..3a5d4df655b 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -3,7 +3,7 @@ package Solution;
 #
 # Package that encapsulates a Visual C++ solution file generation
 #
-# $PostgreSQL: pgsql/src/tools/msvc/Solution.pm,v 1.29 2007/07/12 14:43:21 mha Exp $
+# $PostgreSQL: pgsql/src/tools/msvc/Solution.pm,v 1.30 2007/07/23 10:16:54 mha Exp $
 #
 use Carp;
 use strict;
@@ -124,16 +124,16 @@ s{PG_VERSION_STR "[^"]+"}{__STRINGIFY(x) #x\n#define __STRINGIFY2(z) __STRINGIFY
             print O "#define HAVE_KRB5_ERROR_TEXT_DATA 1\n";
             print O "#define HAVE_KRB5_TICKET_ENC_PART2 1\n";
             print O "#define HAVE_KRB5_FREE_UNPARSED_NAME 1\n";
-            print O "#define PG_KRB_SRVNAM \"postgres\"\n";
-            print O "#define ENABLE_GSS\n";
+            print O "#define ENABLE_GSS 1\n";
+        }
+        print O "#define ENABLE_SSPI 1\n";
+        if (my $port = $self->{options}->{"--with-pgport"})
+        {
+            print O "#undef DEF_PGPORT\n";
+            print O "#undef DEF_PGPORT_STR\n";
+            print O "#define DEF_PGPORT $port\n";
+            print O "#define DEF_PGPORT_STR \"$port\"\n";
         }
-		if (my $port = $self->{options}->{"--with-pgport"})
-		{
-			print O "#undef DEF_PGPORT\n";
-			print O "#undef DEF_PGPORT_STR\n";
-			print O "#define DEF_PGPORT $port\n";
-			print O "#define DEF_PGPORT_STR \"$port\"\n";
-		}
         print O "#define VAL_CONFIGURE \"" . $self->GetFakeConfigure() . "\"\n";
         print O "#endif /* IGNORE_CONFIGURED_SETTINGS */\n";
         close(O);
-- 
GitLab