On Wed, Jul 18, 2007 at 12:16:42PM +0200, Magnus Hagander wrote: > Attached is the patch to support SSPI authentication in libpq. With this > patch, I can authenticate windows clients against a linux server using > kerberos *without* reqiring setting up MIT kerberos on the windows side. > Protocol has not changed at all.
Here's an updated version of this patch. This version has full SSPI support in the server as well, so I can do both kerberos and NTLM between two windows machines using the negotiate method. I've added a libpq connection parameter gsslib and the corresponding environment variable PGGSSLIB. If it's set to "gssapi", libpq will use the MIT GSSAPI implementation to authenticate to GSSAPI servers. If it's not set, or set to anything else, SSPI will be used in Kerberos mode. SSPI in negotiate mode will only be used if the server requests "sspi" authentication instead of "gss". Server-side, I've added the new authentication method "sspi" so the server can inform the client that it wants to do SSPI "negotiate" auth instead of plain Kerberos. Since SSPI and GSSAPI can now both be used, my plan is not to have an autoconf to disable SSPI, but to just enable it unconditionally on win32. Or does this seem like a bad idea? Comments welcome. //Magnus
Index: src/backend/libpq/auth.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/libpq/auth.c,v retrieving revision 1.153 diff -c -r1.153 auth.c *** src/backend/libpq/auth.c 12 Jul 2007 20:36:11 -0000 1.153 --- src/backend/libpq/auth.c 19 Jul 2007 11:25:34 -0000 *************** *** 464,473 **** --- 464,477 ---- /* * 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,542 **** return STATUS_OK; } ! #else /* no ENABLE_GSS */ static int pg_GSS_recvauth(Port *port) { --- 540,546 ---- return STATUS_OK; } ! #else /* no ENABLE_GSS */ static int pg_GSS_recvauth(Port *port) { *************** *** 547,552 **** --- 551,795 ---- } #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,594 **** --- 832,840 ---- 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,694 **** --- 935,945 ---- status = pg_GSS_recvauth(port); break; + case uaSSPI: + sendAuthRequest(port, AUTH_REQ_SSPI); + status = pg_SSPI_recvauth(port); + break; + case uaIdent: /* *************** *** 778,797 **** 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; - 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 --- 1029,1045 ---- else if (areq == AUTH_REQ_CRYPT) pq_sendbytes(&buf, port->cryptSalt, 2); ! #if defined(ENABLE_GSS) || defined(ENABLE_SSPI) /* Add the authentication data for the next step of ! * the GSSAPI or SSPI negotiation. */ else if (areq == AUTH_REQ_GSS_CONT) { if (port->gss->outbuf.length > 0) { 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); } } #endif Index: src/backend/libpq/hba.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/libpq/hba.c,v retrieving revision 1.161 diff -c -r1.161 hba.c *** src/backend/libpq/hba.c 10 Jul 2007 13:14:20 -0000 1.161 --- src/backend/libpq/hba.c 19 Jul 2007 08:28:39 -0000 *************** *** 604,609 **** --- 604,611 ---- *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) Index: src/backend/libpq/pg_hba.conf.sample =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/libpq/pg_hba.conf.sample,v retrieving revision 1.63 diff -c -r1.63 pg_hba.conf.sample *** src/backend/libpq/pg_hba.conf.sample 10 Jul 2007 13:14:20 -0000 1.63 --- src/backend/libpq/pg_hba.conf.sample 19 Jul 2007 10:17:18 -0000 *************** *** 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. # --- 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", "sspi", # "krb5", "ident", "pam" or "ldap". Note that "password" sends passwords # in clear text; "md5" is preferred since it sends encrypted passwords. # Index: src/backend/libpq/pqcomm.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/libpq/pqcomm.c,v retrieving revision 1.193 diff -c -r1.193 pqcomm.c *** src/backend/libpq/pqcomm.c 10 Jul 2007 13:14:20 -0000 1.193 --- src/backend/libpq/pqcomm.c 19 Jul 2007 10:43:27 -0000 *************** *** 173,187 **** { 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); --- 173,193 ---- { 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 /* 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); Index: src/backend/postmaster/postmaster.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/postmaster/postmaster.c,v retrieving revision 1.532 diff -c -r1.532 postmaster.c *** src/backend/postmaster/postmaster.c 11 Jul 2007 08:27:33 -0000 1.532 --- src/backend/postmaster/postmaster.c 19 Jul 2007 11:29:16 -0000 *************** *** 1730,1736 **** /* * Allocate GSSAPI specific state struct */ ! #ifdef ENABLE_GSS port->gss = (pg_gssinfo *)calloc(1, sizeof(pg_gssinfo)); if (!port->gss) { --- 1730,1737 ---- /* * Allocate GSSAPI specific state struct */ ! #ifndef EXEC_BACKEND ! #if defined(ENABLE_GSS) || defined(ENABLE_SSPI) port->gss = (pg_gssinfo *)calloc(1, sizeof(pg_gssinfo)); if (!port->gss) { *************** *** 1740,1745 **** --- 1741,1747 ---- ExitPostmaster(1); } #endif + #endif return port; } *************** *** 3341,3346 **** --- 3343,3361 ---- 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"); Index: src/include/pg_config.h.win32 =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/pg_config.h.win32,v retrieving revision 1.42 diff -c -r1.42 pg_config.h.win32 *** src/include/pg_config.h.win32 16 Apr 2007 18:39:19 -0000 1.42 --- src/include/pg_config.h.win32 18 Jul 2007 10:03:43 -0000 *************** *** 582,588 **** /* Define to the name of the default PostgreSQL service principal in Kerberos. (--with-krb-srvnam=NAME) */ ! #define PG_KRB_SRVNAM "postgres" /* A string containing the version number, platform, and C compiler */ #define PG_VERSION_STR "Uninitialized version string (win32)" --- 582,588 ---- /* Define to the name of the default PostgreSQL service principal in Kerberos. (--with-krb-srvnam=NAME) */ ! #define PG_KRB_SRVNAM "POSTGRES" /* A string containing the version number, platform, and C compiler */ #define PG_VERSION_STR "Uninitialized version string (win32)" Index: src/include/libpq/hba.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/libpq/hba.h,v retrieving revision 1.46 diff -c -r1.46 hba.h *** src/include/libpq/hba.h 10 Jul 2007 13:14:21 -0000 1.46 --- src/include/libpq/hba.h 19 Jul 2007 08:29:21 -0000 *************** *** 23,29 **** uaPassword, uaCrypt, uaMD5, ! uaGSS #ifdef USE_PAM ,uaPAM #endif /* USE_PAM */ --- 23,30 ---- uaPassword, uaCrypt, uaMD5, ! uaGSS, ! uaSSPI #ifdef USE_PAM ,uaPAM #endif /* USE_PAM */ Index: src/include/libpq/libpq-be.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/libpq/libpq-be.h,v retrieving revision 1.61 diff -c -r1.61 libpq-be.h *** src/include/libpq/libpq-be.h 12 Jul 2007 14:43:21 -0000 1.61 --- src/include/libpq/libpq-be.h 19 Jul 2007 11:30:16 -0000 *************** *** 45,50 **** --- 45,66 ---- #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,71 **** /* * 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 --- 75,89 ---- /* * GSSAPI specific state information */ ! #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 */ ! #endif } pg_gssinfo; #endif *************** *** 128,134 **** 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 --- 146,152 ---- int keepalives_interval; int keepalives_count; ! #if defined(ENABLE_GSS) || defined(ENABLE_SSPI) /* * If GSSAPI is supported, store GSSAPI information. * Oterwise, store a NULL pointer to make sure offsets Index: src/include/libpq/pqcomm.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/libpq/pqcomm.h,v retrieving revision 1.105 diff -c -r1.105 pqcomm.h *** src/include/libpq/pqcomm.h 10 Jul 2007 13:14:21 -0000 1.105 --- src/include/libpq/pqcomm.h 19 Jul 2007 08:30:25 -0000 *************** *** 158,163 **** --- 158,164 ---- #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; Index: src/interfaces/libpq/fe-auth.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v retrieving revision 1.127 diff -c -r1.127 fe-auth.c *** src/interfaces/libpq/fe-auth.c 12 Jul 2007 14:43:21 -0000 1.127 --- src/interfaces/libpq/fe-auth.c 19 Jul 2007 11:39:41 -0000 *************** *** 329,339 **** /* * GSSAPI authentication system. */ - #if defined(HAVE_GSSAPI_H) - #include <gssapi.h> - #else - #include <gssapi/gssapi.h> - #endif #if defined(WIN32) && !defined(WIN32_ONLY_COMPILER) /* --- 329,334 ---- *************** *** 378,384 **** * 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) { --- 373,379 ---- * GSSAPI errors contains two parts. Put as much as possible of * both parts into the string. */ ! static void pg_GSS_error(char *mprefix, char *msg, int msglen, OM_uint32 maj_stat, OM_uint32 min_stat) { *************** *** 407,413 **** &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, --- 402,408 ---- &conn->gctx, conn->gtarg_nam, GSS_C_NO_OID, ! GSS_C_MUTUAL_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS, (conn->gctx==GSS_C_NO_CONTEXT)?GSS_C_NO_BUFFER:&conn->ginbuf, *************** *** 504,510 **** return pg_GSS_continue(PQerrormsg, conn); } ! #endif /* * Respond to AUTH_REQ_SCM_CREDS challenge. --- 499,690 ---- return pg_GSS_continue(PQerrormsg, conn); } ! #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,697 **** 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 --- 851,910 ---- return STATUS_ERROR; #endif ! #if defined(ENABLE_GSS) || defined(ENABLE_SSPI) case AUTH_REQ_GSS: { ! 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(); } break; case AUTH_REQ_GSS_CONT: { ! 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(); } break; #else *************** *** 702,707 **** --- 915,944 ---- 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: Index: src/interfaces/libpq/fe-connect.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v retrieving revision 1.349 diff -c -r1.349 fe-connect.c *** src/interfaces/libpq/fe-connect.c 11 Jul 2007 08:27:33 -0000 1.349 --- src/interfaces/libpq/fe-connect.c 19 Jul 2007 11:41:33 -0000 *************** *** 181,192 **** {"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 /* Terminating entry --- MUST BE LAST */ {NULL, NULL, NULL, NULL, NULL, NULL, 0} --- 181,198 ---- {"sslmode", "PGSSLMODE", DefaultSSLMode, NULL, "SSL-Mode", "", 8}, /* sizeof("disable") == 8 */ ! #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,421 **** conn->sslmode = strdup("require"); } #endif ! #if defined(KRB5) || defined(ENABLE_GSS) tmp = conninfo_getval(connOptions, "krbsrvname"); conn->krbsrvname = tmp ? strdup(tmp) : NULL; #endif /* * Free the option info - all is in conn now --- 418,431 ---- conn->sslmode = strdup("require"); } #endif ! #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,1682 **** 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 - --- 1671,1683 ---- return PGRES_POLLING_READING; } } ! #if defined(ENABLE_GSS) || defined(ENABLE_SSPI) /* ! * Continue GSSAPI/SSPI authentication */ if (areq == AUTH_REQ_GSS_CONT) { int llen = msgLength - 4; /* * We can be called repeatedly for the same buffer. * Avoid re-allocating the buffer in this case - *************** *** 2002,2008 **** free(conn->pgpass); if (conn->sslmode) free(conn->sslmode); ! #if defined(KRB5) || defined(ENABLE_GSS) if (conn->krbsrvname) free(conn->krbsrvname); #endif --- 2003,2009 ---- free(conn->pgpass); if (conn->sslmode) free(conn->sslmode); ! #if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI) if (conn->krbsrvname) free(conn->krbsrvname); #endif *************** *** 2031,2036 **** --- 2032,2057 ---- 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) { Index: src/interfaces/libpq/libpq-int.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-int.h,v retrieving revision 1.123 diff -c -r1.123 libpq-int.h *** src/interfaces/libpq/libpq-int.h 12 Jul 2007 14:36:52 -0000 1.123 --- src/interfaces/libpq/libpq-int.h 19 Jul 2007 11:39:59 -0000 *************** *** 52,57 **** --- 52,73 ---- #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,282 **** char *pguser; /* Postgres username and password, if any */ char *pgpass; char *sslmode; /* SSL mode (require,prefer,allow,disable) */ ! #if defined(KRB5) || defined(ENABLE_GSS) char *krbsrvname; /* Kerberos service name */ #endif --- 292,298 ---- char *pguser; /* Postgres username and password, if any */ char *pgpass; char *sslmode; /* SSL mode (require,prefer,allow,disable) */ ! #if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI) char *krbsrvname; /* Kerberos service name */ #endif *************** *** 361,371 **** #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 */ --- 377,399 ---- #ifdef ENABLE_GSS gss_ctx_id_t gctx; /* GSS context */ gss_name_t gtarg_nam; /* GSS target name */ 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,426 **** #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); --- 443,448 ---- Index: src/tools/msvc/Mkvcbuild.pm =================================================================== RCS file: /projects/cvsroot/pgsql/src/tools/msvc/Mkvcbuild.pm,v retrieving revision 1.14 diff -c -r1.14 Mkvcbuild.pm *** src/tools/msvc/Mkvcbuild.pm 7 Jul 2007 07:43:20 -0000 1.14 --- src/tools/msvc/Mkvcbuild.pm 18 Jul 2007 13:28:39 -0000 *************** *** 66,72 **** $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('wldap32.lib') if ($solution->{options}->{ldap}); $postgres->FullExportDLL('postgres.lib'); --- 66,72 ---- $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 secur32.lib'); $postgres->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap}); $postgres->FullExportDLL('postgres.lib'); *************** *** 120,125 **** --- 120,126 ---- $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'); Index: src/tools/msvc/Solution.pm =================================================================== RCS file: /projects/cvsroot/pgsql/src/tools/msvc/Solution.pm,v retrieving revision 1.29 diff -c -r1.29 Solution.pm *** src/tools/msvc/Solution.pm 12 Jul 2007 14:43:21 -0000 1.29 --- src/tools/msvc/Solution.pm 19 Jul 2007 08:46:23 -0000 *************** *** 124,139 **** 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"; } - 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); --- 124,139 ---- 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 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"; } print O "#define VAL_CONFIGURE \"" . $self->GetFakeConfigure() . "\"\n"; print O "#endif /* IGNORE_CONFIGURED_SETTINGS */\n"; close(O);
---------------------------(end of broadcast)--------------------------- TIP 9: In versions below 8.0, the planner will ignore your desire to choose an index scan if your joining column's datatypes do not match