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 + #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 - #else - #include - #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 + #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 #include *************** *** 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);