aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMagnus Hagander <magnus@hagander.net>2007-07-23 10:16:54 +0000
committerMagnus Hagander <magnus@hagander.net>2007-07-23 10:16:54 +0000
commitf70866fb2353dba162fc296f644e7ce77af6d79f (patch)
treeb061ad6406fcd27c42b2fb3c210925c7b11dfb33 /src
parenta0dab332a2e1961f45b38b23bd428859621e6f3c (diff)
downloadpostgresql-f70866fb2353dba162fc296f644e7ce77af6d79f.tar.gz
postgresql-f70866fb2353dba162fc296f644e7ce77af6d79f.zip
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.
Diffstat (limited to 'src')
-rw-r--r--src/backend/libpq/auth.c262
-rw-r--r--src/backend/libpq/hba.c4
-rw-r--r--src/backend/libpq/pg_hba.conf.sample2
-rw-r--r--src/backend/libpq/pqcomm.c10
-rw-r--r--src/backend/postmaster/postmaster.c19
-rw-r--r--src/include/libpq/hba.h5
-rw-r--r--src/include/libpq/libpq-be.h26
-rw-r--r--src/include/libpq/pqcomm.h3
-rw-r--r--src/interfaces/libpq/fe-auth.c277
-rw-r--r--src/interfaces/libpq/fe-connect.c51
-rw-r--r--src/interfaces/libpq/libpq-int.h40
-rw-r--r--src/tools/msvc/Mkvcbuild.pm5
-rw-r--r--src/tools/msvc/Solution.pm20
13 files changed, 648 insertions, 76 deletions
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)
{
@@ -1743,6 +1744,7 @@ ConnCreate(int serverFd)
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
@@ -2031,6 +2032,26 @@ freePGconn(PGconn *conn)
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);