/*#---------------------------------------------------------------------------- ** ** File: sspcalls.c ** ** Synopsis: This module contains SSPI function calls for SSPI SPM DLL. ** ** Copyright (C) 1995 Microsoft Corporation. All Rights Reserved. ** ** Authors: LucyC Created 25 Sept. 1995 ** **---------------------------------------------------------------------------*/ #include "msnspmh.h" #include #include "urlmon.h" BOOL g_fUUEncodeData = TRUE; typedef enum _COMPUTER_NAME_FORMAT { ComputerNameNetBIOS, ComputerNameDnsHostname, ComputerNameDnsDomain, ComputerNameDnsFullyQualified, ComputerNamePhysicalNetBIOS, ComputerNamePhysicalDnsHostname, ComputerNamePhysicalDnsDomain, ComputerNamePhysicalDnsFullyQualified, ComputerNameMax } COMPUTER_NAME_FORMAT ; typedef BOOL (WINAPI * PFN_GET_COMPUTER_NAME_EX)( IN COMPUTER_NAME_FORMAT NameType, OUT LPSTR lpBuffer, IN OUT LPDWORD nSize ); PFN_GET_COMPUTER_NAME_EX g_pfnGetComputerNameExA = NULL; DWORD GetZoneFromUrl(LPSTR pszUrl); /*----------------------------------------------------------------------------- ** ** Function: GetSecAuthMsg ** ** Synopsis: This function generates a SSPI NEGOTIATE or RESPONSE ** authorization string for the specified SSPI package. ** The authorization string generated by this function ** follows the format: ** " " ** If global uuencoding is turned on, this functions will ** uuencode the message before building it into an ** authorization string; by default, the uuencoding flag is ** always on. ** This functions calls InitializeSecurityContext() to ** generate the NEGOTIATE/RESPONSE message for the authori- ** zation string. If the SSPI function returns NO_CREDENTIAL, ** and if the PROMPT_CREDS flag is not turned on when blocking ** is permitted, this function will call the SSPI function ** again with the PROMPT_CREDS flag set; if SSPI returns ** NO_CREDENTIAL again, this SSPI will return ERROR to the ** caller. ** ** ** Arguments: ** ** pData - pointer to SspData containing the SSPI function table ** and the SSPI package list. ** pkgID - the package index of the SSPI package to use. ** pInContext - pointer to a context handle. If NULL is specified, ** this function will use a temporary space for the context ** handle and delete the handle before returning to the ** caller. If non-NULL address is specified, the context ** handle created by the SSPI is returned to the caller. ** And the caller will have to delete the handle when it's ** done with it. ** fContextReq - the SSPI request flag to pass to InitializeSecurityContext ** pBuffIn - pointer to the uudecoded CHALLENGE message if any. ** For generating NEGOTIATE message, this pointer should be NULL. ** cbBuffIn - length of the CHALLENGE message. This should be zero when ** when pBuffIn is NULL. ** pFinalBuff - pointer to a buffer for the final authorization string. ** pszTarget - Server Host Name ** bNonBlock - a flag which is set if blocking is not permitted. ** ** Return Value: ** ** SPM_STATUS_OK - if an authorization string is generated successfully ** SPM_STATUS_WOULD_BLOCK - if generating an authorization string would ** cause blocking when blocking is not permitted. ** SPM_ERROR - if any problem/error is encountered in generating an ** authorization string, including user hitting cancel on ** the SSPI dialog prompt for name/password. ** **---------------------------------------------------------------------------*/ DWORD GetSecAuthMsg ( PSspData pData, PCredHandle pCredential, DWORD pkgID, // the package index into package list PCtxtHandle pInContext, PCtxtHandle pOutContext, ULONG fContextReq, // Request Flags VOID *pBuffIn, DWORD cbBuffIn, char *pFinalBuff, DWORD *pcbBuffOut, SEC_CHAR *pszTarget, // Server Host Name UINT bNonBlock, LPSTR pszScheme, PCSTR lpszUrl, SECURITY_STATUS *pssResult ) { // char szDecodedBuf[MAX_BLOB_SIZE]; // char *szDecodedBuf; // char FastDecodedBuf[MAX_BLOB_SIZE]; char *SlowDecodedBuf = NULL; int retsize; SECURITY_STATUS SecStat; TimeStamp Lifetime; SecBufferDesc OutBuffDesc; SecBuffer OutSecBuff; SecBufferDesc InBuffDesc; SecBuffer InSecBuff; ULONG ContextAttributes; // char OutBufPlain[MAX_AUTH_MSG_SIZE]; char *SlowOutBufPlain = NULL; char *pOutMsg = NULL; DWORD RetStatus; long maxbufsize; CHAR szDecoratedTarget[MAX_PATH + 6]; DWORD cbTarget; ULONG cbMaxToken; // // BUGBUG: Deal with output buffer not being long enough if (pFinalBuff == NULL) { return(SPM_ERROR); } // // Prepare our output buffer. We use a temporary buffer because // the real output buffer will most likely need to be uuencoded // OutBuffDesc.ulVersion = 0; OutBuffDesc.cBuffers = 1; OutBuffDesc.pBuffers = &OutSecBuff; OutSecBuff.cbBuffer = MAX_AUTH_MSG_SIZE; OutSecBuff.BufferType = SECBUFFER_TOKEN; // Always use the slow alloc'ed buf as a quick fix to appease // DAV redir stress scenarios, which run in svchost and starts // with a 4KB stack limit. cbMaxToken = GetPkgMaxToken( pkgID ); SlowOutBufPlain = (char *) ALLOCATE_FIXED_MEMORY(cbMaxToken); if( SlowOutBufPlain == NULL ) { RetStatus = SPM_STATUS_INSUFFICIENT_BUFFER; goto Cleanup; } OutSecBuff.pvBuffer = SlowOutBufPlain; OutSecBuff.cbBuffer = cbMaxToken; // // Prepare our Input buffer if a CHALLENGE message is passed in. // if ( pBuffIn ) { InBuffDesc.ulVersion = 0; InBuffDesc.cBuffers = 1; InBuffDesc.pBuffers = &InSecBuff; InSecBuff.BufferType = SECBUFFER_TOKEN; // // If this is UUENCODED, decode it first // if ( g_fUUEncodeData) { DWORD cbDecodedBuf; cbDecodedBuf = cbBuffIn; SlowDecodedBuf = ALLOCATE_FIXED_MEMORY(cbDecodedBuf); if( SlowDecodedBuf == NULL ) { RetStatus = SPM_STATUS_INSUFFICIENT_BUFFER; goto Cleanup; } InSecBuff.cbBuffer = HTUU_decode (pBuffIn, SlowDecodedBuf, cbDecodedBuf); InSecBuff.pvBuffer = SlowDecodedBuf; } else { InSecBuff.cbBuffer = cbBuffIn; InSecBuff.pvBuffer = pBuffIn; } } // If scheme is Negotiate, set ISC_REQ_MUTUAL_AUTH and decorate // the server name indicated by pszTarget by appending a '$' to the // server name. if (pszScheme && !(lstrcmpi(pszScheme, "Negotiate"))) { fContextReq |= ISC_REQ_MUTUAL_AUTH; cbTarget = (pszTarget ? strlen(pszTarget) : 0); if (cbTarget && (cbTarget <= MAX_PATH - sizeof( "HTTP/" ))) { memcpy(szDecoratedTarget, "HTTP/", sizeof( "HTTP/" ) - 1 ); memcpy(szDecoratedTarget + sizeof( "HTTP/" ) - 1, pszTarget, cbTarget + 1); pszTarget = szDecoratedTarget; // OutputDebugStringA(pszTarget); } } // // Call SSPI function generate the NEGOTIATE/RESPONSE message // if (fContextReq & ISC_REQ_DELEGATE) { // we should only request delegation when calling InitializeSecurityContext if // the site is in the intranet or trusted sites zone. Otherwise you will be giving // the user's TGT to any web server that is trusted for delegation. DWORD dwZone = GetZoneFromUrl((PSTR)lpszUrl); if ((dwZone != URLZONE_INTRANET) && (dwZone != URLZONE_TRUSTED)) { fContextReq &= ~ISC_REQ_DELEGATE; } } SspiRetry: // // BUGBUG: Same credential handle could be used by multiple threads at the // same time. // SecStat = (*(pData->pFuncTbl->InitializeSecurityContext))( pCredential, pInContext, pszTarget, fContextReq, 0, SECURITY_NATIVE_DREP, (pBuffIn) ? &InBuffDesc : NULL, 0, pOutContext, &OutBuffDesc, &ContextAttributes, &Lifetime ); *pssResult = SecStat; // // If SSPI function fails // if ( !NT_SUCCESS( SecStat ) ) { RetStatus = SPM_ERROR; // // If SSPI do not have user name/password for the secified package, // if ((SecStat == SEC_E_NO_CREDENTIALS) || (g_fIsWhistler && (SecStat == SEC_E_LOGON_DENIED))) { // // If we have prompted the user and still get back "No Credential" // error, it means the user does not have valid credential; the // user hit on the UI box. If we have supplied a valid // credential, but get back a "No Credential" error, then something // has gone wrong; we definitely should return to caller with ERROR // if ((fContextReq & ISC_REQ_PROMPT_FOR_CREDS) || (fContextReq & ISC_REQ_USE_SUPPLIED_CREDS)) { RetStatus = SPM_ERROR; // return ERROR to caller } else if (bNonBlock) { // // Blocking is not permitted, return WOULD_BLOCK to caller // RetStatus = SPM_STATUS_WOULD_BLOCK; } else { // Blocking is permitted and we have not asked the SSPI to // prompt the user for proper credential, we should call // the SSPI again with PROMPT_CREDS flag set. // fContextReq = fContextReq | ISC_REQ_PROMPT_FOR_CREDS; goto SspiRetry; } } SetLastError( SecStat ); goto Cleanup; } RetStatus = SPM_STATUS_OK; #if 0 // // note: when support for processing final MUTUAL_AUTH blob is added, // will need to allow for non-existent output buffer. // if( OutSecBuff.cbBuffer == 0 ) { *pcbBuffOut = 0; goto Cleanup; } #endif // // Only return the SSPI blob if a output buffer is specified // if (pFinalBuff) { // // Initialize the final buffer to hold the package name followed by // a space. And setup the pOutMsg pointer to points to the character // following the space so that the final NEGOTIATE/RESPONSE can be // copied into the pFinalBuff starting at the character pointed to // by pOutMsg. // wsprintf (pFinalBuff, "%s ", pData->PkgList[pkgID]->pName); pOutMsg = pFinalBuff + lstrlen(pFinalBuff); if ( g_fUUEncodeData) { maxbufsize = *pcbBuffOut - lstrlen(pData->PkgList[pkgID]->pName) - 1; // // uuencode it, but make sure that it fits in the given buffer // retsize = HTUU_encode ((BYTE *) OutSecBuff.pvBuffer, OutSecBuff.cbBuffer, (CHAR *) pOutMsg, maxbufsize); if (retsize > 0) *pcbBuffOut = retsize + lstrlen(pData->PkgList[pkgID]->pName)+1; else RetStatus = SPM_STATUS_INSUFFICIENT_BUFFER; } else if ( *pcbBuffOut >= lstrlen(pData->PkgList[pkgID]->pName) + OutSecBuff.cbBuffer + 1 ) { CopyMemory( (CHAR *) pOutMsg, OutSecBuff.pvBuffer, OutSecBuff.cbBuffer ); *pcbBuffOut = lstrlen(pData->PkgList[pkgID]->pName) + 1 + OutSecBuff.cbBuffer; } else { *pcbBuffOut = lstrlen(pData->PkgList[pkgID]->pName) + OutSecBuff.cbBuffer + 1; RetStatus = SPM_STATUS_INSUFFICIENT_BUFFER; } } Cleanup: if( SlowOutBufPlain != NULL ) { FREE_MEMORY( SlowOutBufPlain ); } if( SlowDecodedBuf != NULL ) { FREE_MEMORY( SlowDecodedBuf ); } return (RetStatus); }