/*++ Copyright (c) 1998 Microsoft Corporation Module Name: digesta.cxx Abstract: sspi ansi interface for digest package. Author: Adriaan Canter (adriaanc) 01-Aug-1998 --*/ #include "include.hxx" static SecurityFunctionTableA SecTableA = { SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION, EnumerateSecurityPackagesA, NULL, // QueryCredentialsAttributesA AcquireCredentialsHandleA, FreeCredentialsHandle, NULL, // SspiLogonUserA InitializeSecurityContextA, AcceptSecurityContext, CompleteAuthToken, DeleteSecurityContext, ApplyControlToken, QueryContextAttributesA, ImpersonateSecurityContext, RevertSecurityContext, MakeSignature, VerifySignature, FreeContextBuffer, QuerySecurityPackageInfoA, NULL, // Reserved3 NULL, // Reserved4 NULL, // ExportSecurityContext NULL, // ImportSecurityContextA NULL, // Reserved7 NULL, // Reserved8 NULL, // QuerySecurityContextToken NULL, // EncryptMessage NULL // DecryptMessage }; //-------------------------------------------------------------------------- // // Function: InitSecurityInterfaceA // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- extern "C" PSecurityFunctionTableA SEC_ENTRY InitSecurityInterfaceA(VOID) { PSecurityFunctionTableA pSecTableA = &SecTableA; return pSecTableA; } //-------------------------------------------------------------------------- // // Function: AcquireCredentialsHandleA // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // HEINOUS SSPI HACK here: AcquireCredentialsHandle is called with the package // name ("Digest") as the package identifier. When AcquireCredentialsHandle returns // to the caller PCredHandle->dwLower is set by security.dll to be the index of // the package returned. EnumerateSecurityPackages. This is how SSPI resolves the // correct provider dll when subsequent calls are made through the dispatch table // (PSecurityFunctionTale). Any credential *or* context handle handed out by the // package must have the dwLower member set to this index so that subsequent calls // can resolve the dll from the handle. // //-------------------------------------------------------------------------- extern "C" SECURITY_STATUS SEC_ENTRY AcquireCredentialsHandleA( LPSTR pszPrincipal, // Name of principal LPSTR pszPackageName, // Name of package DWORD dwCredentialUse, // Flags indicating use VOID SEC_FAR * pvLogonId, // Pointer to logon ID VOID SEC_FAR * pAuthData, // Package specific data SEC_GET_KEY_FN pGetKeyFn, // Pointer to GetKey() func VOID SEC_FAR * pvGetKeyArgument, // Value to pass to GetKey() PCredHandle phCredential, // (out) Cred Handle PTimeStamp ptsExpiry // (out) Lifetime (optional) ) { if (!InitGlobals()) return SEC_E_INTERNAL_ERROR; SECURITY_STATUS ssResult; // Outbound credentials only. if (!(dwCredentialUse & SECPKG_CRED_OUTBOUND) || (dwCredentialUse & SECPKG_CRED_INBOUND)) { DIGEST_ASSERT(FALSE); ssResult = SEC_E_UNKNOWN_CREDENTIALS; goto exit; } // Logon to cache. // Logon to the cache and get the session context. CSess *pSess; PSEC_WINNT_AUTH_IDENTITY_EXA pSecIdExA; PSEC_WINNT_AUTH_IDENTITY pSecId; // HTTP clients will pass in this structure. pSecIdExA = (PSEC_WINNT_AUTH_IDENTITY_EXA) pAuthData; // Non-HTTP clients (OE4, OE5) will pass in this structure. pSecId = (PSEC_WINNT_AUTH_IDENTITY) pAuthData; // Check for HTTP client application logon. if (pAuthData && (pSecIdExA->Version == sizeof(SEC_WINNT_AUTH_IDENTITY_EXA)) && pSecIdExA->User && pSecIdExA->UserLength == sizeof(DIGEST_PKG_DATA)) { DIGEST_PKG_DATA *pPkgData; pPkgData = (DIGEST_PKG_DATA*) pSecIdExA->User; pSess = g_pCache->LogOnToCache(pPkgData->szAppCtx, pPkgData->szUserCtx, TRUE); } // Check for non-HTTP client application logon. else { // Find or create the single non-HTTP session. pSess = g_pCache->LogOnToCache(NULL, NULL, FALSE); // If user+pass+realm (domain) is passed in, create and // attach a matching credential to this session. if (pAuthData && pSecId->User && pSecId->UserLength && pSecId->Domain && pSecId->DomainLength && pSecId->Password && pSecId->PasswordLength) { // Create a credential with the information passed in. CCred *pCred; CCredInfo *pInfo; pInfo = new CCredInfo(NULL, (LPSTR) pSecId->Domain, (LPSTR) pSecId->User, (LPSTR) pSecId->Password, NULL, NULL); if (pInfo) { pCred = g_pCache->CreateCred(pSess, pInfo); delete pInfo; } } } // BUGBUG - return better error codes. if (!pSess) { DIGEST_ASSERT(FALSE); ssResult = SEC_E_INTERNAL_ERROR; goto exit; } // Hand out the session handle. phCredential->dwUpper = g_pCache->MapSessionToHandle(pSess); // ***** phCredential->dwLower will be set by security.dll ***** ssResult = SEC_E_OK; exit: return ssResult; } //-------------------------------------------------------------------------- // // Function: FreeCredentialsHandle // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- extern "C" SECURITY_STATUS SEC_ENTRY FreeCredentialsHandle(PCredHandle phCredential) { // bugbug - asserted. if (!InitGlobals()) return SEC_E_INTERNAL_ERROR; SECURITY_STATUS ssResult; // Get the session context from the handle. CSess *pSess; pSess = g_pCache->MapHandleToSession(phCredential->dwUpper); if (!pSess) { DIGEST_ASSERT(FALSE); ssResult = SEC_E_UNKNOWN_CREDENTIALS; goto exit; } // Logoff from the cache. if (g_pCache->LogOffFromCache(pSess) != ERROR_SUCCESS) { DIGEST_ASSERT(FALSE); ssResult = SEC_E_INTERNAL_ERROR; goto exit; } ssResult = SEC_E_OK; exit: return ssResult; } //-------------------------------------------------------------------------- // // Function: InitializeSecurityContextA // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // //-------------------------------------------------------------------------- extern "C" SECURITY_STATUS SEC_ENTRY InitializeSecurityContextA( PCredHandle phCredential, // Cred to base context PCtxtHandle phContext, // Existing context (OPT) LPSTR pszTargetName, // Name of target DWORD fContextReq, // Context Requirements DWORD Reserved1, // Reserved, MBZ DWORD TargetDataRep, // Data rep of target PSecBufferDesc pInput, // Input Buffers DWORD Reserved2, // Reserved, MBZ PCtxtHandle phNewContext, // (out) New Context handle PSecBufferDesc pOutput, // (inout) Output Buffers DWORD SEC_FAR * pfContextAttr, // (out) Context attrs PTimeStamp ptsExpiry // (out) Life span (OPT) ) { if (!InitGlobals()) return SEC_E_INTERNAL_ERROR; LPSTR szHost, szRealm, szUser, szPass, szNonce; DWORD cbHost, cbRealm, cbUser, cbPass, cbNonce; LPSTR szCtx = NULL; SECURITY_STATUS ssResult = SEC_E_OK; // Client nonce NULL except for md5-sess. LPSTR szCNonce = NULL; CSess *pSess; CCred *pCred; CParams *pParams = NULL; CCredInfo *pInfo = NULL; // Rude credential flush for all apps. if (!phCredential && (fContextReq & ISC_REQ_NULL_SESSION)) { g_pCache->FlushCreds(NULL, NULL); ssResult = SEC_E_OK; goto exit; } // Get the session pointer from the handle. pSess = g_pCache->MapHandleToSession(phCredential->dwUpper); if (!pSess) { DIGEST_ASSERT(FALSE); ssResult = SEC_E_UNKNOWN_CREDENTIALS; goto exit; } // Legacy conn. oriented client may require a continue // message on null buffer input. if (!pSess->fHTTP && !pInput && pOutput) { *((LPDWORD) (pOutput->pBuffers[0].pvBuffer)) = 0; pOutput->pBuffers[0].cbBuffer = sizeof(DWORD); ssResult = SEC_I_CONTINUE_NEEDED; goto exit; } // Flush creds for indicated session. if (fContextReq & ISC_REQ_NULL_SESSION) { g_pCache->FlushCreds(pSess, NULL); ssResult = SEC_E_OK; goto exit; } DIGEST_ASSERT(phCredential && pInput && pOutput); // Parse the challenge to a params object. if (CDigest::ParseChallenge(pSess, pInput, &pParams, fContextReq) != ERROR_SUCCESS) { // DIGEST_ASSERT(FALSE); ssResult = SEC_E_INVALID_TOKEN; goto exit; } // Get host, realm (required) and any nonce, user & pass. pParams->GetParam(CParams::HOST, &szHost, &cbHost); pParams->GetParam(CParams::REALM, &szRealm, &cbRealm); pParams->GetParam(CParams::NONCE, &szNonce, &cbNonce); pParams->GetParam(CParams::USER, &szUser, &cbUser); pParams->GetParam(CParams::PASS, &szPass, &cbPass); // If prompting UI is indicated. if (fContextReq & ISC_REQ_PROMPT_FOR_CREDS) { CCredInfo *pInfoIn, *pInfoOut; // Attempt to get one or more cred infos pInfoIn = g_pCache->FindCred(pSess, szHost, szRealm, szUser, NULL, NULL, FIND_CRED_UI); // Get the persistence key from pSess szCtx = CSess::GetCtx(pSess); DIGEST_ASSERT(szCtx); // If this is prompting for UI specifying md5-sess, // create a client nonce to associate with cred. if (pParams->IsMd5Sess()) szCNonce = CDigest::MakeCNonce(); pParams->GetParam(CParams::HOST, &szHost, &cbHost); // Prompt with authentication dialog. if (DigestErrorDlg(szCtx, szHost, szRealm, szUser, szNonce, szCNonce, pInfoIn, &pInfoOut, pParams->GetHwnd()) == ERROR_SUCCESS) { DIGEST_ASSERT(pInfoOut); // Create the credential. pCred = g_pCache->CreateCred(pSess, pInfoOut); // Record that the host is trusted. if (pSess->fHTTP) CCredCache::SetTrustedHostInfo(szCtx, pParams); } else { ssResult = SEC_E_NO_CREDENTIALS; goto exit; } // Retrieve the credentials just created. pInfo = g_pCache->FindCred(pSess, szHost, szRealm, pInfoOut->szUser, szNonce, szCNonce, FIND_CRED_AUTH); // Clean up one or more cred infos. // BUGBUG - null out pointers after freeing. while (pInfoIn) { CCredInfo *pNext; pNext = pInfoIn->pNext; delete pInfoIn; pInfoIn = pNext; } if (pInfoOut) delete pInfoOut; if (szCNonce) delete szCNonce; if (!pInfo) { ssResult = SEC_E_NO_CREDENTIALS; goto exit; } } // Otherwise we are attempting to authenticate. We may be either // authenticating in response to a challenge or pre-authenticating. else { // Get the persistence key from pSess szCtx = CSess::GetCtx(pSess); DIGEST_ASSERT(szCtx); // For HTTP sessions we check the trusted host list unless // 1) credentials are supplied, or 2) a context has been passed // in which specifically instructs to ignore the host list. if (pSess->fHTTP && !pParams->IsPreAuth() && !pParams->AreCredsSupplied()) { if (!phContext || !(phContext->dwUpper & DIGEST_PKG_FLAGS_IGNORE_TRUSTED_HOST_LIST)) { if (!CCredCache::IsTrustedHost(szCtx, szHost)) { ssResult = SEC_E_NO_CREDENTIALS; goto exit; } } } // If preauthenticating. if (pParams->IsPreAuth()) { // If using supplied credentials. if (pParams->AreCredsSupplied()) { // Create a cred info using supplied values. Include passed-in NC. pInfo = new CCredInfo(szHost, szRealm, szUser, szPass, szNonce, szCNonce); pInfo->cCount = pParams->GetNC(); if (!(pInfo && pInfo->dwStatus == ERROR_SUCCESS)) { DIGEST_ASSERT(FALSE); ssResult = SEC_E_INTERNAL_ERROR; goto exit; } } // Otherwise attempt to find cred info in cache. else { // Attempt to find the credentials from realm and any user. pInfo = g_pCache->FindCred(pSess, szHost, szRealm, szUser, NULL, NULL, FIND_CRED_PREAUTH); } // Return if no credentials exist. if (!pInfo) { ssResult = SEC_E_NO_CREDENTIALS; goto exit; } } // Otherwise auth in response to challenge. else { // Check if logoff is requested. CHAR* szLogoff; szLogoff = pParams->GetParam(CParams::LOGOFF); if (szLogoff && !lstrcmpi(szLogoff, "TRUE")) { g_pCache->FlushCreds(NULL, szRealm); ssResult = SEC_E_CONTEXT_EXPIRED; goto exit; } // If a context is passed in examine the stale header unless specifically // directed not to. if (pSess->fHTTP && phContext && !pParams->AreCredsSupplied() && !(phContext->dwUpper & DIGEST_PKG_FLAGS_IGNORE_STALE_HEADER)) { CHAR* szStale; DWORD cbStale; pParams->GetParam(CParams::STALE, &szStale, &cbStale); if (!szStale || !lstrcmpi(szStale, "FALSE")) { ssResult = SEC_E_NO_CREDENTIALS; goto exit; } } // If this is authenticating specifying md5-sess, // create a client nonce to associate with cred. if (pParams->IsMd5Sess()) szCNonce = CDigest::MakeCNonce(); // If credentials are supplied, create an entry in // the credential cache. We search as usual subsequently. if (pParams->AreCredsSupplied()) { // Create a cred info using supplied values. pInfo = new CCredInfo(szHost, szRealm, szUser, szPass, szNonce, szCNonce); if (!(pInfo && pInfo->dwStatus == ERROR_SUCCESS)) { DIGEST_ASSERT(FALSE); ssResult = SEC_E_INTERNAL_ERROR; goto exit; } pCred = g_pCache->CreateCred(pSess, pInfo); delete pInfo; } // Attempt to find the credentials from realm and any user. pInfo = g_pCache->FindCred(pSess, szHost, szRealm, szUser, szNonce, szCNonce, FIND_CRED_AUTH); // Return if no credentials exist. if (!pInfo) { ssResult = SEC_E_NO_CREDENTIALS; goto exit; } } } // We should now have the appropriate cred info. Generate the response. DIGEST_ASSERT(pInfo); if (CDigest::GenerateResponse(pSess, pParams, pInfo, pOutput) != ERROR_SUCCESS) { DIGEST_ASSERT(FALSE); ssResult = SEC_E_INTERNAL_ERROR; goto exit; } // Delete cred info if allocated. // bugbug - move further down. if (pInfo) delete pInfo; ssResult = SEC_E_OK; exit: if ((ssResult != SEC_E_OK) && (ssResult != SEC_I_CONTINUE_NEEDED)) pOutput->pBuffers[0].cbBuffer = 0; // BUGBUG - delete pInfo if not NULL. // Delete persistence key if allocated. if (szCtx) delete szCtx; // Identify the new context. if (phNewContext && phCredential) phNewContext->dwLower = phCredential->dwLower; // Delete the params object. if (pParams) delete pParams; return ssResult; } //-------------------------------------------------------------------------- // // Function: AcceptSecurityContext // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- extern "C" SECURITY_STATUS SEC_ENTRY AcceptSecurityContext( PCredHandle phCredential, // Cred to base context PCtxtHandle phContext, // Existing context (OPT) PSecBufferDesc pInput, // Input buffer unsigned long fContextReq, // Context Requirements unsigned long TargetDataRep, // Target Data Rep PCtxtHandle phNewContext, // (out) New context handle PSecBufferDesc pOutput, // (inout) Output buffers unsigned long SEC_FAR * pfContextAttr, // (out) Context attributes PTimeStamp ptsExpiry // (out) Life span (OPT) ) { // BUGBUG - don't need initglobals. if (!InitGlobals()) return SEC_E_INTERNAL_ERROR; return(SEC_E_UNSUPPORTED_FUNCTION); } //-------------------------------------------------------------------------- // // Function: DeleteSecurityContext // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- extern "C" SECURITY_STATUS SEC_ENTRY DeleteSecurityContext( PCtxtHandle phContext // Context to delete ) { if (!InitGlobals()) return SEC_E_INTERNAL_ERROR; return SEC_E_UNSUPPORTED_FUNCTION; } //-------------------------------------------------------------------------- // // Function: ApplyControlToken // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- extern "C" SECURITY_STATUS SEC_ENTRY ApplyControlToken( PCtxtHandle phContext, // Context to modify PSecBufferDesc pInput // Input token to apply ) { if (!InitGlobals()) return SEC_E_INTERNAL_ERROR; SECURITY_STATUS ssResult; // Current flags used are // DIGEST_PKG_FLAG_IGNORE_TRUSTED_HOST_LIST // DIGEST_PKG_FLAG_IGNORE_STALE_HEADER phContext->dwUpper |= *((LPDWORD) (pInput->pBuffers[0].pvBuffer)); ssResult = SEC_E_OK; return ssResult; } //-------------------------------------------------------------------------- // // Function: EnumerateSecurityPackagesA // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- SECURITY_STATUS SEC_ENTRY EnumerateSecurityPackagesA(DWORD SEC_FAR *pcPackages, PSecPkgInfoA SEC_FAR *ppSecPkgInfo) { SECURITY_STATUS ssResult; // BUGBUG - ALLOW ASSERTS? ssResult = QuerySecurityPackageInfoA(PACKAGE_NAME, ppSecPkgInfo); if (ssResult == SEC_E_OK) { *pcPackages = 1; } return ssResult; } //-------------------------------------------------------------------------- // // Function: QuerySecurityPackageInfoA // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- SECURITY_STATUS SEC_ENTRY QuerySecurityPackageInfoA(LPSTR szPackageName, PSecPkgInfoA SEC_FAR *ppSecPkgInfo) { // BUGBUG - ALLOW ASSERTS? PSecPkgInfoA pSecPkgInfo; DWORD cbSecPkgInfo; SECURITY_STATUS ssResult; LPSTR pCur; if (strcmp(szPackageName, PACKAGE_NAME)) { ssResult = SEC_E_SECPKG_NOT_FOUND; goto exit; } cbSecPkgInfo = sizeof(SecPkgInfoA) + sizeof(PACKAGE_NAME) + sizeof(PACKAGE_COMMENT); pSecPkgInfo = (PSecPkgInfoA) LocalAlloc(0,cbSecPkgInfo); if (!pSecPkgInfo) { ssResult = SEC_E_INSUFFICIENT_MEMORY; goto exit; } pSecPkgInfo->fCapabilities = PACKAGE_CAPABILITIES; pSecPkgInfo->wVersion = PACKAGE_VERSION; pSecPkgInfo->wRPCID = PACKAGE_RPCID; pSecPkgInfo->cbMaxToken = PACKAGE_MAXTOKEN; pCur = (LPSTR) (pSecPkgInfo) + sizeof(SecPkgInfoA); pSecPkgInfo->Name = pCur; memcpy(pSecPkgInfo->Name, PACKAGE_NAME, sizeof(PACKAGE_NAME)); pCur += sizeof(PACKAGE_NAME); pSecPkgInfo->Comment = pCur; memcpy(pSecPkgInfo->Comment, PACKAGE_COMMENT, sizeof(PACKAGE_COMMENT)); *ppSecPkgInfo = pSecPkgInfo; ssResult = SEC_E_OK; exit: return ssResult; } //-------------------------------------------------------------------------- // // Function: FreeContextBuffer // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- extern "C" SECURITY_STATUS SEC_ENTRY FreeContextBuffer(void SEC_FAR *pvContextBuffer) { if (!InitGlobals()) return SEC_E_INTERNAL_ERROR; LocalFree(pvContextBuffer); return SEC_E_OK; } //-------------------------------------------------------------------------- // // Function: CompleteAuthToken // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- extern "C" SECURITY_STATUS SEC_ENTRY CompleteAuthToken( PCtxtHandle phContext, // Context to complete PSecBufferDesc pToken // Token to complete ) { if (!InitGlobals()) return SEC_E_INTERNAL_ERROR; return SEC_E_UNSUPPORTED_FUNCTION; } //-------------------------------------------------------------------------- // // Function: ImpersonateSecurityContext // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- extern "C" SECURITY_STATUS SEC_ENTRY ImpersonateSecurityContext( PCtxtHandle phContext // Context to impersonate ) { if (!InitGlobals()) return SEC_E_INTERNAL_ERROR; return SEC_E_UNSUPPORTED_FUNCTION; } //-------------------------------------------------------------------------- // // Function: RevertSecurityContext // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- extern "C" SECURITY_STATUS SEC_ENTRY RevertSecurityContext( PCtxtHandle phContext // Context from which to re ) { if (!InitGlobals()) return SEC_E_INTERNAL_ERROR; return SEC_E_UNSUPPORTED_FUNCTION; } //-------------------------------------------------------------------------- // // Function: QueryContextAttributesA // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- extern "C" SECURITY_STATUS SEC_ENTRY QueryContextAttributesA( PCtxtHandle phContext, // Context to query unsigned long ulAttribute, // Attribute to query void SEC_FAR * pBuffer // Buffer for attributes ) { if (!InitGlobals()) return SEC_E_INTERNAL_ERROR; return SEC_E_UNSUPPORTED_FUNCTION; } //-------------------------------------------------------------------------- // // Function: MakeSignature // // Synopsis: // // Effects: // // Arguments: [phContext] -- context to use // [fQOP] -- quality of protection to use // [pMessage] -- message // [MessageSeqNo] -- sequence number of message // // Requires: // // Returns: // // Notes: // //-------------------------------------------------------------------------- extern "C" SECURITY_STATUS SEC_ENTRY MakeSignature( PCtxtHandle phContext, ULONG fQOP, PSecBufferDesc pMessage, ULONG MessageSeqNo) { if (!InitGlobals()) return SEC_E_INTERNAL_ERROR; return SEC_E_UNSUPPORTED_FUNCTION; } //-------------------------------------------------------------------------- // // Function: VerifySignature // // Synopsis: // // Effects: // // Arguments: [phContext] -- Context performing the unseal // [pMessage] -- Message to verify // [MessageSeqNo] -- Sequence number of this message // [pfQOPUsed] -- quality of protection used // // Requires: // // Returns: // // Notes: // //-------------------------------------------------------------------------- extern "C" SECURITY_STATUS SEC_ENTRY VerifySignature(PCtxtHandle phContext, PSecBufferDesc pMessage, ULONG MessageSeqNo, ULONG * pfQOP) { if (!InitGlobals()) return SEC_E_INTERNAL_ERROR; return SEC_E_UNSUPPORTED_FUNCTION; }