//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1995. // // File: ctxtapi.c // // Contents: // // Classes: // // Functions: // // History: 10-02-96 RichardW Created // //---------------------------------------------------------------------------- #include "sslp.h" #include #include #include #include #include NTSTATUS NTAPI SslPurgeSessionCache( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferSize, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus); NTSTATUS NTAPI SslSessionCacheInfo( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferSize, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus); NTSTATUS NTAPI SslGetPerfmonInfo( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferSize, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus); SECURITY_STATUS SEC_ENTRY SpInitLsaModeContext( LSA_SEC_HANDLE dwCredHandle, LSA_SEC_HANDLE dwCtxtHandle, PSECURITY_STRING pszTargetName, ULONG fContextReq, ULONG TargetDataRep, PSecBufferDesc pInput, PLSA_SEC_HANDLE pdwNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry, PBYTE pfMapContext, PSecBuffer pContextData) { PSPContext pContext = NULL; PSPCredentialGroup pCred = NULL; SPBuffer CommOut; SPBuffer CommIn; PSecBuffer pInToken = NULL; PSecBuffer pOutToken = NULL; PSecBuffer pExtra = NULL; DWORD fAttr = ISC_RET_REPLAY_DETECT | ISC_RET_SEQUENCE_DETECT | ISC_RET_CONFIDENTIALITY | ISC_RET_STREAM; DWORD fSchContext = CONTEXT_FLAG_CLIENT; NTSTATUS Status; ANSI_STRING String; int i; SP_STATUS pctRet = PCT_ERR_OK; #if DBG DebugLog((DEB_TRACE, "SpInitLsaModeContext(0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\n", dwCredHandle, dwCtxtHandle, pszTargetName, fContextReq, TargetDataRep, pInput, pdwNewContext, pOutput, pfContextAttr, ptsExpiry, pfMapContext, pContextData)); if(pszTargetName) { DebugLog((DEB_TRACE, "pszTargetName:<%ls>\n",pszTargetName->Buffer)); } #endif /* These flags are never allowed */ if (fContextReq & (ISC_REQ_DELEGATE | ISC_REQ_PROMPT_FOR_CREDS )) { TRACE_EXIT( SpInitLsaModeContext, SEC_E_UNSUPPORTED_FUNCTION ); return SP_LOG_RESULT( SEC_E_UNSUPPORTED_FUNCTION ); } /* Initialize output buffer locations */ for (i = 0; i < (int)pOutput->cBuffers; i++ ) { switch( ( pOutput->pBuffers[i].BufferType ) & (~(SECBUFFER_ATTRMASK))) { case SECBUFFER_EMPTY: if(!pOutToken && (fContextReq & ISC_REQ_ALLOCATE_MEMORY)) pOutToken = &pOutput->pBuffers[i]; break; case SECBUFFER_TOKEN: pOutToken = &pOutput->pBuffers[i]; break; case SECBUFFER_DATA: case SECBUFFER_STREAM_HEADER: case SECBUFFER_STREAM_TRAILER: default: break; } } if ( pOutToken == NULL ) { TRACE_EXIT( SpInitLsaModeContext, SEC_E_INVALID_TOKEN ); return SP_LOG_RESULT( SEC_E_INVALID_TOKEN ); } pOutToken->BufferType = SECBUFFER_TOKEN; if ( fContextReq & ISC_REQ_ALLOCATE_MEMORY ) { CommOut.pvBuffer = NULL; CommOut.cbBuffer = 0; CommOut.cbData = 0; fAttr |= ISC_RET_ALLOCATED_MEMORY; pOutToken->pvBuffer = NULL; pOutToken->cbBuffer = 0; } else { if ( pOutToken->pvBuffer == NULL ) { TRACE_EXIT( SpInitLsaModeContext, SEC_E_INSUFFICIENT_MEMORY ); return SP_LOG_RESULT( SEC_E_INSUFFICIENT_MEMORY ); } } CommOut.pvBuffer = pOutToken->pvBuffer; CommOut.cbBuffer = pOutToken->cbBuffer; CommOut.cbData = 0; if ( fContextReq & (ISC_REQ_EXTENDED_ERROR) ) { fAttr |= ISC_RET_EXTENDED_ERROR; fSchContext |= CONTEXT_FLAG_EXT_ERR; } if ( fContextReq & (ISC_REQ_CONNECTION) ) { fAttr |= ISC_REQ_CONNECTION; fSchContext |= CONTEXT_FLAG_CONNECTION_MODE; } if ( fContextReq & (ISC_REQ_MUTUAL_AUTH) ) { // Validate the server certificate. fAttr |= ISC_RET_MUTUAL_AUTH; fSchContext |= CONTEXT_FLAG_MUTUAL_AUTH; fSchContext &= ~CONTEXT_FLAG_MANUAL_CRED_VALIDATION; if ( fContextReq & (ISC_REQ_MANUAL_CRED_VALIDATION)) { // These flags are mutually exclusive return SP_LOG_RESULT( SEC_E_UNSUPPORTED_FUNCTION ); } } else { if ( fContextReq & (ISC_REQ_MANUAL_CRED_VALIDATION)) { fAttr |= ISC_RET_MANUAL_CRED_VALIDATION; fSchContext |= CONTEXT_FLAG_MANUAL_CRED_VALIDATION; } // Turn off automatic credential validation if so specified in registry. if(g_fManualCredValidation) { fSchContext |= CONTEXT_FLAG_MANUAL_CRED_VALIDATION; } } if ( fContextReq & (ISC_REQ_USE_SUPPLIED_CREDS)) { fAttr |= ISC_REQ_USE_SUPPLIED_CREDS; fSchContext |= CONTEXT_FLAG_NO_INCOMPLETE_CRED_MSG; } if( pfContextAttr ) { *pfContextAttr = fAttr; } if ( dwCtxtHandle == 0 ) { pContext = SPContextCreate( pszTargetName->Buffer ); if(pContext == NULL) { TRACE_EXIT( SpInitLsaModeContext, SEC_E_INSUFFICIENT_MEMORY ); return SP_LOG_RESULT( SEC_E_INSUFFICIENT_MEMORY ); } pContext->Flags |= fSchContext; pCred = (PSPCredentialGroup) dwCredHandle ; if(pctRet == PCT_ERR_OK) { pctRet = SPContextSetCredentials(pContext, pCred); } if(pctRet != PCT_ERR_OK) { SPContextDelete(pContext); *pdwNewContext = 0; TRACE_EXIT( SpInitLsaModeContext, PctTranslateError( pctRet )); return SP_LOG_RESULT( PctTranslateError(pctRet) ); } pctRet = pContext->InitiateHello( pContext, &CommOut, TRUE); if ( (CommOut.cbBuffer == 0) && (pctRet == PCT_INT_BUFF_TOO_SMALL) ) { SPContextDelete(pContext); TRACE_EXIT( SpInitLsaModeContext, SEC_E_INSUFFICIENT_MEMORY ); return SP_LOG_RESULT( SEC_E_INSUFFICIENT_MEMORY ); } if ( pctRet != PCT_ERR_OK ) { SPContextDelete(pContext); *pdwNewContext = 0; TRACE_EXIT( SpInitLsaModeContext, PctTranslateError( pctRet) ); return SP_LOG_RESULT( PctTranslateError(pctRet) ); } if ( fContextReq & ISC_REQ_ALLOCATE_MEMORY ) { // // Easy: The caller asked for us to allocate memory for them, so // let the LSA do it. // pOutToken->pvBuffer = CommOut.pvBuffer ; } else { // // The caller has a buffer that we're supposed to use. Make sure we // can fit. // if ( (ULONG) CommOut.cbBuffer <= pOutToken->cbBuffer ) { RtlCopyMemory( pOutToken->pvBuffer, CommOut.pvBuffer, CommOut.cbBuffer ); } else { DebugLog(( DEB_TRACE, "Supplied buffer is too small\n" )); SPContextDelete( pContext ); TRACE_EXIT( SpInitLsaModeContext, SEC_E_INSUFFICIENT_MEMORY ); return SP_LOG_RESULT( SEC_E_INSUFFICIENT_MEMORY ); } } *pdwNewContext = (LSA_SEC_HANDLE) pContext ; pOutToken->pvBuffer = CommOut.pvBuffer; pOutToken->cbBuffer = CommOut.cbData; #if DBG DebugLog(( DEB_TRACE, "Output: type:0x%8.8x, pv:0x%8.8x, cb:0x%x\n", pOutToken->BufferType, pOutToken->pvBuffer, pOutToken->cbBuffer)); if(pOutToken->pvBuffer) { DBG_HEX_STRING(DEB_BUFFERS, pOutToken->pvBuffer, pOutToken->cbBuffer); } #endif } else { /* Initialize input buffer locations */ for (i = 0; i < (int)pInput->cBuffers; i++ ) { switch( (pInput->pBuffers[i].BufferType & (~SECBUFFER_ATTRMASK)) ) { case SECBUFFER_TOKEN: case SECBUFFER_TOKEN | SECBUFFER_READONLY: pInToken = &pInput->pBuffers[i]; break; case SECBUFFER_EMPTY: if(!pInToken) { pInToken = &pInput->pBuffers[i]; } else if(!pExtra) { pExtra = &pInput->pBuffers[i]; } break; case SECBUFFER_DATA: case SECBUFFER_STREAM_HEADER: case SECBUFFER_STREAM_TRAILER: default: break; } } #if DBG if(pInToken) { DebugLog(( DEB_TRACE, "Input: type:0x%8.8x, pv:0x%8.8x, cb:0x%x\n", pInToken->BufferType, pInToken->pvBuffer, pInToken->cbBuffer)); if(pInToken->pvBuffer) { DBG_HEX_STRING(DEB_BUFFERS, pInToken->pvBuffer, pInToken->cbBuffer); } } if(pExtra) { DebugLog(( DEB_TRACE, "Extra: type:0x%8.8x, pv:0x%8.8x, cb:0x%x\n", pExtra->BufferType, pExtra->pvBuffer, pExtra->cbBuffer)); } #endif if(pInToken == NULL) { CommIn.pvBuffer = NULL; CommIn.cbBuffer = 0; CommIn.cbData = 0; } else { CommIn.pvBuffer = pInToken->pvBuffer; CommIn.cbBuffer = pInToken->cbBuffer; CommIn.cbData = pInToken->cbBuffer; } pContext = (PSPContext) dwCtxtHandle ; if ( dwCredHandle ) { pCred = (PSPCredentialGroup) dwCredHandle ; } if( pContext == NULL || pCred == NULL ) { TRACE_EXIT( SpInitLsaModeContext, SEC_E_INVALID_HANDLE ); return SP_LOG_RESULT( SEC_E_INVALID_HANDLE ); } pContext->Flags |= fSchContext; pctRet = SPContextSetCredentials(pContext, pCred); if(pctRet == PCT_ERR_OK) { // HACKHACK - adjust SSL3/TLS1 state if(pContext->State == SSL3_STATE_RENEGOTIATE) { pContext->State = SSL3_STATE_GEN_HELLO_REQUEST; } pctRet = pContext->ProtocolHandler( pContext, &CommIn, &CommOut); } if(pctRet == PCT_INT_INCOMPLETE_MSG) { if(pExtra) { pExtra->BufferType = SECBUFFER_MISSING | SECBUFFER_UNMAPPED ; pExtra->cbBuffer = CommIn.cbData - pInToken->cbBuffer; pExtra->pvBuffer = NULL ; } } else { pOutToken->pvBuffer = CommOut.pvBuffer; pOutToken->cbBuffer = CommOut.cbData; } if(pctRet == PCT_INT_BUFF_TOO_SMALL) { pOutToken->BufferType |= SECBUFFER_UNMAPPED; } #if DBG if(pOutToken) { DebugLog(( DEB_TRACE, "Output: type:0x%8.8x, pv:0x%8.8x, cb:0x%x\n", pOutToken->BufferType, pOutToken->pvBuffer, pOutToken->cbBuffer)); if(pOutToken->pvBuffer) { DBG_HEX_STRING(DEB_BUFFERS, pOutToken->pvBuffer, pOutToken->cbBuffer); } } if(pExtra) { DebugLog(( DEB_TRACE, "Extra: type:0x%8.8x, pv:0x%8.8x, cb:0x%x\n", pExtra->BufferType, pExtra->pvBuffer, pExtra->cbBuffer)); } #endif if(!SP_FATAL(pctRet)) { *pdwNewContext = dwCtxtHandle ; } if(PCT_ERR_OK != pctRet) { TRACE_EXIT( SpInitLsaModeContext, PctTranslateError( pctRet )); return SP_LOG_RESULT( PctTranslateError(pctRet) ); } if(pInToken) { if(CommIn.cbData < pInToken->cbBuffer && pExtra) { pExtra->BufferType = SECBUFFER_EXTRA | SECBUFFER_UNMAPPED ; pExtra->cbBuffer = pInToken->cbBuffer - CommIn.cbData; pExtra->pvBuffer = NULL ; } } } if ( (pContext->State == SP_STATE_CONNECTED) && ( (pContext->Flags & CONTEXT_FLAG_MAPPED) == 0 ) ) { // // Need to map the context back down to the user process. It // doesn't get any scarier than this: // *pfMapContext = TRUE ; DebugOut(( DEB_TRACE, "Mapping context to usermode\n" )); pctRet = SPContextSerialize(pContext, SslRelocateToken, (PUCHAR *) &pContextData->pvBuffer, &pContextData->cbBuffer, TRUE); if(PCT_ERR_OK != pctRet) { return SP_LOG_RESULT( PctTranslateError(pctRet) ); } pContext->Flags |= CONTEXT_FLAG_MAPPED ; LogHandshakeInfoEvent(pContext->RipeZombie->fProtocol, pContext->pCipherInfo, pContext->pHashInfo, pContext->pKeyExchInfo, pContext->RipeZombie->dwExchStrength); } if(ptsExpiry != NULL) { if(pContext->RipeZombie->pRemoteCert != NULL) { ptsExpiry->QuadPart = *((LONGLONG *)&pContext->RipeZombie->pRemoteCert->pCertInfo->NotAfter); } else { ptsExpiry->QuadPart = MAXTIMEQUADPART; } } if(pContext->State == SP_STATE_CONNECTED || pContext->State == SP_STATE_SHUTDOWN) { return SEC_E_OK; } else { return SEC_I_CONTINUE_NEEDED; } } SECURITY_STATUS SEC_ENTRY SpMoveContextToUser( ULONG dwCtxtHandle, PSecBuffer pContextBuffer) { return(SEC_E_UNSUPPORTED_FUNCTION); } SECURITY_STATUS SEC_ENTRY SpDeleteContext( LSA_SEC_HANDLE dwCtxtHandle) { PSPContext pContext ; DebugLog((DEB_TRACE, "SpDeleteContext(0x%x)\n", dwCtxtHandle)); pContext = (PSPContext) dwCtxtHandle ; SPContextDelete( pContext ); return( SEC_E_OK ); } SECURITY_STATUS SEC_ENTRY SpApplyControlToken( LSA_SEC_HANDLE dwCtxtHandle, PSecBufferDesc pInput) { PSPContext pContext; PSecBuffer Buffer ; DWORD cbState; DebugLog((DEB_TRACE, "SpApplyControlToken(0x%x, 0x%x)\n", dwCtxtHandle, pInput)); pContext = (PSPContext) dwCtxtHandle ; if ( pInput->cBuffers != 1 ) { return SP_LOG_RESULT(SEC_E_INVALID_TOKEN); } Buffer = pInput->pBuffers ; if(Buffer->cbBuffer < sizeof(DWORD)) { return SP_LOG_RESULT(SEC_E_UNSUPPORTED_FUNCTION); } switch(*(DWORD *)Buffer->pvBuffer) { case SCHANNEL_RENEGOTIATE: { PDWORD RedoData; PBYTE pbReadKey; DWORD cbReadKey; DebugLog((DEB_TRACE, "SCHANNEL_RENEGOTIATE\n")); if(Buffer->cbBuffer < sizeof(DWORD) * 2) { return SP_LOG_RESULT(SEC_E_INVALID_TOKEN); } RedoData = (DWORD *)Buffer->pvBuffer; if(RedoData[1] != SSL3_STATE_RENEGOTIATE && RedoData[1] != PCT1_STATE_RENEGOTIATE) { return SP_LOG_RESULT(SEC_E_UNSUPPORTED_FUNCTION); } pContext->State = RedoData[1]; return SEC_E_OK; } case SCHANNEL_SHUTDOWN: DebugLog((DEB_TRACE, "SCHANNEL_SHUTDOWN\n")); pContext->State = SP_STATE_SHUTDOWN_PENDING; return SEC_E_OK; case SCHANNEL_ALERT: { SCHANNEL_ALERT_TOKEN *pAlertToken; DebugLog((DEB_TRACE, "SCHANNEL_TLS1_ALERT\n")); if(Buffer->cbBuffer < sizeof(SCHANNEL_ALERT_TOKEN)) { return SP_LOG_RESULT(SEC_E_INVALID_TOKEN); } pAlertToken = (SCHANNEL_ALERT_TOKEN *)Buffer->pvBuffer; // Alerts are only supported in SSL3 and TLS1 if(!(pContext->RipeZombie->fProtocol & SP_PROT_SSL3TLS1)) { return SP_LOG_RESULT(SEC_E_UNSUPPORTED_FUNCTION); } // Reality check alert values. if(pAlertToken->dwAlertType >= 0x100 || pAlertToken->dwAlertNumber >= 0x100) { return SP_LOG_RESULT(SEC_E_INVALID_TOKEN); } SetTls1Alert(pContext, (BYTE)pAlertToken->dwAlertType, (BYTE)pAlertToken->dwAlertNumber); #if DBG DebugLog((DEB_TRACE, "AlertLevel:0x%x, AlertNumber:0x%x\n", pAlertToken->dwAlertType, pAlertToken->dwAlertNumber)); #endif return SEC_E_OK; } case SCHANNEL_SESSION: { SCHANNEL_SESSION_TOKEN *pSessionToken; SECURITY_STATUS Status = SEC_E_UNSUPPORTED_FUNCTION; DebugLog((DEB_TRACE, "SCHANNEL_SESSION\n")); if(pContext->RipeZombie == NULL) { return SP_LOG_RESULT(SEC_E_INVALID_HANDLE); } if(Buffer->cbBuffer < sizeof(SCHANNEL_SESSION_TOKEN)) { return SP_LOG_RESULT(SEC_E_INVALID_TOKEN); } pSessionToken = (SCHANNEL_SESSION_TOKEN *)Buffer->pvBuffer; if(pSessionToken->dwFlags & SSL_SESSION_DISABLE_RECONNECTS) { // Disable reconnects pContext->RipeZombie->ZombieJuju = FALSE; Status = SEC_E_OK; } if(pSessionToken->dwFlags & SSL_SESSION_ENABLE_RECONNECTS) { // Enable reconnects if(pContext->RipeZombie->DeferredJuju) { pContext->RipeZombie->ZombieJuju = TRUE; pContext->RipeZombie->DeferredJuju = FALSE; Status = SEC_E_OK; } } return Status; } default: return SP_LOG_RESULT(SEC_E_UNSUPPORTED_FUNCTION); } } VOID SEC_ENTRY SpLogonTerminated(PLUID pLogonId) { return; } SECURITY_STATUS SEC_ENTRY SpAcceptLsaModeContext( LSA_SEC_HANDLE dwCredHandle, LSA_SEC_HANDLE dwCtxtHandle, PSecBufferDesc pInput, ULONG fContextReq, ULONG TargetDataRep, PLSA_SEC_HANDLE pdwNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry, PBYTE pfMapContext, PSecBuffer pContextData) { PSPContext pContext = NULL; PSPCredentialGroup pCred = NULL; SPBuffer CommOut; SPBuffer CommIn; PSecBuffer pInToken = NULL; PSecBuffer pOutToken = NULL; PSecBuffer pExtra = NULL; unsigned long fAttr = ASC_RET_REPLAY_DETECT | ASC_RET_SEQUENCE_DETECT | ASC_RET_CONFIDENTIALITY | ASC_RET_STREAM; DWORD fSchContext = 0; int i; SP_STATUS pctRet = PCT_ERR_OK; TRACE_ENTER( SpAcceptLsaModeContext ); #if DBG DebugLog((DEB_TRACE, "SpAcceptLsaModeContext(0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\n", dwCredHandle, dwCtxtHandle, pInput, fContextReq, TargetDataRep, pdwNewContext, pOutput, pfContextAttr, ptsExpiry, pfMapContext, pContextData)); #endif // This flag is never allowed if(fContextReq & ASC_REQ_DELEGATE) { TRACE_EXIT( SpAcceptLsaModeContext, SEC_E_UNSUPPORTED_FUNCTION ); return SEC_E_UNSUPPORTED_FUNCTION; } if ( fContextReq & ASC_REQ_MUTUAL_AUTH ) { fSchContext |= CONTEXT_FLAG_MUTUAL_AUTH; } /* Initialize input buffer locations */ for (i = 0; i < (int)pInput->cBuffers; i++ ) { switch( pInput->pBuffers[i].BufferType & (~(SECBUFFER_ATTRMASK)) ) { case SECBUFFER_TOKEN: case SECBUFFER_TOKEN | SECBUFFER_READONLY: pInToken = &pInput->pBuffers[i]; break; case SECBUFFER_EMPTY: if(!pInToken) { pInToken = &pInput->pBuffers[i]; } else if(!pExtra) { pExtra = &pInput->pBuffers[i]; } break; case SECBUFFER_DATA: case SECBUFFER_STREAM_HEADER: case SECBUFFER_STREAM_TRAILER: default: break; } } #if DBG if(pInToken) { DebugLog(( DEB_TRACE, "Input: type:0x%8.8x, pv:0x%8.8x, cb:0x%x\n", pInToken->BufferType, pInToken->pvBuffer, pInToken->cbBuffer)); if(pInToken->pvBuffer) { DBG_HEX_STRING(DEB_BUFFERS, pInToken->pvBuffer, pInToken->cbBuffer); } } if(pExtra) { DebugLog(( DEB_TRACE, "Extra: type:0x%8.8x, pv:0x%8.8x, cb:0x%x\n", pExtra->BufferType, pExtra->pvBuffer, pExtra->cbBuffer)); } #endif /* Initialize output buffer locations */ for (i = 0; i < (int) pOutput->cBuffers; i++ ) { switch( pOutput->pBuffers[i].BufferType & (~(SECBUFFER_ATTRMASK)) ) { case SECBUFFER_EMPTY: if(!pOutToken && (fContextReq & ASC_REQ_ALLOCATE_MEMORY)) pOutToken = &pOutput->pBuffers[i]; break; case SECBUFFER_TOKEN: pOutToken = &pOutput->pBuffers[i]; break; case SECBUFFER_DATA: case SECBUFFER_STREAM_HEADER: case SECBUFFER_STREAM_TRAILER: default: break; } } if(pOutToken == NULL) { TRACE_EXIT( SpAcceptLsaModeContext, SEC_E_INVALID_TOKEN ); return SEC_E_INVALID_TOKEN; } if ( !pExtra ) { DebugOut(( DEB_TRACE, " Warning - no Empty security buffer\n")); } pOutToken->BufferType = SECBUFFER_TOKEN; if(pInToken && pInToken->BufferType == SECBUFFER_TOKEN) { CommIn.pvBuffer = pInToken->pvBuffer; CommIn.cbBuffer = pInToken->cbBuffer; CommIn.cbData = pInToken->cbBuffer; } else { CommIn.pvBuffer = NULL; CommIn.cbBuffer = 0; CommIn.cbData = 0; } if (fContextReq & ASC_REQ_ALLOCATE_MEMORY) { fAttr |= ASC_RET_ALLOCATED_MEMORY; pOutToken->pvBuffer = NULL; pOutToken->cbBuffer = 0; } CommOut.pvBuffer = pOutToken->pvBuffer; CommOut.cbBuffer = pOutToken->cbBuffer; CommOut.cbData = 0; if (fContextReq & (ASC_REQ_EXTENDED_ERROR)) { fAttr |= ASC_RET_EXTENDED_ERROR; fSchContext |= CONTEXT_FLAG_EXT_ERR; } if ( fContextReq & (ASC_REQ_CONNECTION) ) { fAttr |= ASC_RET_CONNECTION; fSchContext |= CONTEXT_FLAG_CONNECTION_MODE; } if (pfContextAttr) { *pfContextAttr = fAttr; } if ( dwCtxtHandle == 0 ) { pContext = SPContextCreate(NULL); if (pContext == NULL) { TRACE_EXIT( SpAcceptLsaModeContext, SEC_E_INSUFFICIENT_MEMORY ); return SEC_E_INSUFFICIENT_MEMORY; } } else { pContext = (PSPContext) dwCtxtHandle ; } if ( dwCredHandle == 0 ) { pCred = NULL ; } else { pCred = (PSPCredentialGroup) dwCredHandle ; } if ( (pContext == NULL) || (pCred == NULL) ) { if ( dwCtxtHandle == 0 ) { SPContextDelete( pContext ); } TRACE_EXIT( SpAcceptLsaModeContext, SEC_E_INVALID_HANDLE ); return( SEC_E_INVALID_HANDLE ); } pctRet = SPContextSetCredentials(pContext, pCred); pContext->Flags |= fSchContext; if ( pctRet == PCT_ERR_OK ) { pctRet = pContext->ProtocolHandler( pContext, &CommIn, &CommOut); } if ( dwCtxtHandle == 0 ) { if ( pctRet != PCT_ERR_OK ) { SPContextDelete( pContext ); } else { *pdwNewContext = (LSA_SEC_HANDLE) pContext ; } } else { *pdwNewContext = (LSA_SEC_HANDLE) pContext ; } if (CommOut.cbData == 0 && pctRet == PCT_INT_BUFF_TOO_SMALL) { TRACE_EXIT( SpAcceptLsaModeContext, SEC_E_INSUFFICIENT_MEMORY ); return SEC_E_INSUFFICIENT_MEMORY; } if (pctRet == PCT_INT_INCOMPLETE_MSG) { if(pExtra) { pExtra->BufferType = SECBUFFER_MISSING | SECBUFFER_UNMAPPED ; pExtra->cbBuffer = CommIn.cbData - pInToken->cbBuffer; pExtra->pvBuffer = NULL ; DebugOut(( DEB_TRACE, "Incomplete message, needs %d more bytes\n", pExtra->cbBuffer )); } else { DebugOut(( DEB_TRACE, "No Empty buffer for returning missing info!\n" )); } } else { pOutToken->pvBuffer = CommOut.pvBuffer; pOutToken->cbBuffer = CommOut.cbData; } if(pctRet == PCT_INT_BUFF_TOO_SMALL) { pOutToken->BufferType |= SECBUFFER_UNMAPPED; } if(pOutToken->cbBuffer == 0) { // Don't return an output token if the output buffer is // empty. Also, make sure that the extended error flag is // turned off. pOutToken->BufferType = SECBUFFER_EMPTY; if(pfContextAttr) { *pfContextAttr &= ~ASC_RET_EXTENDED_ERROR; } } if (PCT_ERR_OK != pctRet) { TRACE_EXIT( SpAcceptLsaModeContext, PctTranslateError( pctRet ) ); return PctTranslateError(pctRet); } if(pInToken) { if (CommIn.cbData < pInToken->cbBuffer && pExtra) { pExtra->BufferType = SECBUFFER_EXTRA | SECBUFFER_UNMAPPED ; pExtra->cbBuffer = pInToken->cbBuffer - CommIn.cbData; pExtra->pvBuffer = NULL ; DebugOut(( DEB_TRACE, "Extra data, needs to be mapped back: %d\n", pExtra->cbBuffer )); } } if ( (pContext->State == SP_STATE_CONNECTED) && ( (pContext->Flags & CONTEXT_FLAG_MAPPED) == 0 ) ) { // // Need to map the context back down to the user process. It // doesn't get any scarier than this: // *pfMapContext = TRUE ; DebugOut(( DEB_TRACE, "Mapping context to usermode\n" )); pctRet = SPContextSerialize(pContext, SslRelocateToken, (PUCHAR *) &pContextData->pvBuffer, &pContextData->cbBuffer, TRUE); if (PCT_ERR_OK != pctRet) { TRACE_EXIT( SpAcceptLsaModeContext, PctTranslateError( pctRet ) ); return PctTranslateError(pctRet); } pContext->Flags |= CONTEXT_FLAG_MAPPED ; LogHandshakeInfoEvent(pContext->RipeZombie->fProtocol, pContext->pCipherInfo, pContext->pHashInfo, pContext->pKeyExchInfo, pContext->RipeZombie->dwExchStrength); } #if DBG if(pOutToken) { DebugLog(( DEB_TRACE, "Output: type:0x%8.8x, pv:0x%8.8x, cb:0x%x\n", pOutToken->BufferType, pOutToken->pvBuffer, pOutToken->cbBuffer)); if(pOutToken->pvBuffer) { DBG_HEX_STRING(DEB_BUFFERS, pOutToken->pvBuffer, pOutToken->cbBuffer); } } if(pExtra) { DebugLog(( DEB_TRACE, "Extra: type:0x%8.8x, pv:0x%8.8x, cb:0x%x\n", pExtra->BufferType, pExtra->pvBuffer, pExtra->cbBuffer)); } if(pContext->State == SP_STATE_CONNECTED) { DebugLog((DEB_TRACE, "Server handshake complete\n")); } #endif if(ptsExpiry != NULL) { if(pContext->RipeZombie->pRemoteCert != NULL) { ptsExpiry->QuadPart = *((LONGLONG *)&pContext->RipeZombie->pRemoteCert->pCertInfo->NotAfter); } else { ptsExpiry->QuadPart = MAXTIMEQUADPART; } } if(pContext->State == SP_STATE_CONNECTED && pContext->RipeZombie->hLocator) { // Certificate mapping was successful. *pfContextAttr |= ASC_RET_MUTUAL_AUTH; } if(pContext->State == SP_STATE_CONNECTED || pContext->State == SP_STATE_SHUTDOWN) { return SEC_E_OK; } else { return SEC_I_CONTINUE_NEEDED; } } NTSTATUS SpCallPackage( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { PULONG Request ; if ( !ProtocolSubmitBuffer ) { return SEC_E_UNSUPPORTED_FUNCTION ; } if(!SchannelInit(FALSE)) { return SP_LOG_RESULT(SEC_E_UNSUPPORTED_FUNCTION); } Request = (PULONG) ProtocolSubmitBuffer ; if ( *Request == SSL_LOOKUP_CERT_MESSAGE ) { return SslDoClientRequest( ClientRequest, ProtocolSubmitBuffer, ClientBufferBase, SubmitBufferLength, ProtocolReturnBuffer, ReturnBufferLength, ProtocolStatus ); } else if ( *Request == SSL_LOOKUP_EXTERNAL_CERT_MESSAGE && ClientBufferBase == ProtocolSubmitBuffer) { // This function is only allowed to be called from the // lsass.exe process. return SslMapExternalCredential( ClientRequest, ProtocolSubmitBuffer, ClientBufferBase, SubmitBufferLength, ProtocolReturnBuffer, ReturnBufferLength, ProtocolStatus ); } else if( *Request == SSL_CACHE_INFO_MESSAGE ) { return SslSessionCacheInfo( ClientRequest, ProtocolSubmitBuffer, ClientBufferBase, SubmitBufferLength, ProtocolReturnBuffer, ReturnBufferLength, ProtocolStatus ); } else if( *Request == SSL_PURGE_CACHE_MESSAGE ) { return SslPurgeSessionCache( ClientRequest, ProtocolSubmitBuffer, ClientBufferBase, SubmitBufferLength, ProtocolReturnBuffer, ReturnBufferLength, ProtocolStatus ); } return( SEC_E_UNSUPPORTED_FUNCTION ); } NTSTATUS SpCallPackageUntrusted( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { ULONG MessageType; // // Get the messsage type from the protocol submit buffer. // if(SubmitBufferLength < sizeof(ULONG)) { return STATUS_INVALID_PARAMETER; } MessageType = *((ULONG *)(ProtocolSubmitBuffer)); if(!SchannelInit(FALSE)) { return SP_LOG_RESULT(SEC_E_UNSUPPORTED_FUNCTION); } // // Allow the dispatch routines to only set the return buffer information // on success conditions. // *ProtocolReturnBuffer = NULL; *ReturnBufferLength = 0; // // Process message as appropriate. // switch(MessageType) { case SSL_PURGE_CACHE_MESSAGE: return SslPurgeSessionCache( ClientRequest, ProtocolSubmitBuffer, ClientBufferBase, SubmitBufferLength, ProtocolReturnBuffer, ReturnBufferLength, ProtocolStatus); case SSL_CACHE_INFO_MESSAGE: return SslSessionCacheInfo( ClientRequest, ProtocolSubmitBuffer, ClientBufferBase, SubmitBufferLength, ProtocolReturnBuffer, ReturnBufferLength, ProtocolStatus); case SSL_PERFMON_INFO_MESSAGE: return SslGetPerfmonInfo( ClientRequest, ProtocolSubmitBuffer, ClientBufferBase, SubmitBufferLength, ProtocolReturnBuffer, ReturnBufferLength, ProtocolStatus); default: return( SEC_E_UNSUPPORTED_FUNCTION ); } } NTSTATUS NTAPI SslPurgeSessionCache( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferSize, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus) { NTSTATUS Status; SECPKG_CALL_INFO CallInfo; SECPKG_CLIENT_INFO ClientInfo; PLUID LogonId; SSL_PURGE_SESSION_CACHE_REQUEST PurgeRequest; PSSL_PURGE_SESSION_CACHE_REQUEST pPurgeRequest; // // Verify the request. // DebugLog((DEB_TRACE, "Purging session cache\n")); if(!LsaTable->GetCallInfo(&CallInfo)) { Status = STATUS_INTERNAL_ERROR; goto Cleanup; } if(CallInfo.Attributes & SECPKG_CALL_WOWCLIENT) { PSSL_PURGE_SESSION_CACHE_REQUEST_WOW64 pRequest; if(SubmitBufferSize < sizeof(SSL_PURGE_SESSION_CACHE_REQUEST_WOW64)) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; } pRequest = (PSSL_PURGE_SESSION_CACHE_REQUEST_WOW64)ProtocolSubmitBuffer; memset(&PurgeRequest, 0, sizeof(PurgeRequest)); PurgeRequest.MessageType = pRequest->MessageType; PurgeRequest.LogonId = pRequest->LogonId; PurgeRequest.Flags = pRequest->Flags; PurgeRequest.ServerName.Length = pRequest->ServerName.Length; PurgeRequest.ServerName.MaximumLength = pRequest->ServerName.MaximumLength; PurgeRequest.ServerName.Buffer = (PVOID) UlongToPtr(pRequest->ServerName.Buffer); pPurgeRequest = &PurgeRequest; } else { if (SubmitBufferSize < sizeof(SSL_PURGE_SESSION_CACHE_REQUEST)) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; } pPurgeRequest = (PSSL_PURGE_SESSION_CACHE_REQUEST) ProtocolSubmitBuffer; } // // Normalize the strings // NULL_RELOCATE_ONE(&pPurgeRequest->ServerName); // // Find the callers logon id & TCB status // Status = LsaTable->GetClientInfo(&ClientInfo); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Verify the caller has TCB privilege if they want to purge someone // else's session cache entries. // if(!RtlIsZeroLuid(&pPurgeRequest->LogonId) || (pPurgeRequest->Flags & SSL_PURGE_CLIENT_ALL_ENTRIES) || (pPurgeRequest->Flags & SSL_PURGE_SERVER_ALL_ENTRIES)) { if(!ClientInfo.HasTcbPrivilege) { Status = STATUS_PRIVILEGE_NOT_HELD; goto Cleanup; } } // // If the caller did not provide a logon id, use the caller's logon id. // if(RtlIsZeroLuid(&pPurgeRequest->LogonId)) { LogonId = &ClientInfo.LogonId; } else { LogonId = &pPurgeRequest->LogonId; } // // Purge the requested cache entries. // Status = SPCachePurgeEntries(LogonId, ClientInfo.ProcessID, pPurgeRequest->ServerName.Buffer, pPurgeRequest->Flags); *ProtocolReturnBuffer = NULL; *ReturnBufferLength = 0; Cleanup: *ProtocolStatus = Status; return(STATUS_SUCCESS); } NTSTATUS NTAPI SslSessionCacheInfo( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferSize, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus) { NTSTATUS Status; SECPKG_CALL_INFO CallInfo; SECPKG_CLIENT_INFO ClientInfo; PLUID LogonId; SSL_SESSION_CACHE_INFO_REQUEST InfoRequest; PSSL_SESSION_CACHE_INFO_REQUEST pInfoRequest; PSSL_SESSION_CACHE_INFO_RESPONSE pInfoResponse = NULL; DWORD cbInfoResponse; PVOID pvClient; *ProtocolReturnBuffer = NULL; *ReturnBufferLength = 0; // // Verify the request. // if(!LsaTable->GetCallInfo(&CallInfo)) { Status = STATUS_INTERNAL_ERROR; goto Cleanup; } if(CallInfo.Attributes & SECPKG_CALL_WOWCLIENT) { PSSL_SESSION_CACHE_INFO_REQUEST_WOW64 pRequest; if(SubmitBufferSize < sizeof(SSL_SESSION_CACHE_INFO_REQUEST_WOW64)) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; } pRequest = (PSSL_SESSION_CACHE_INFO_REQUEST_WOW64)ProtocolSubmitBuffer; memset(&InfoRequest, 0, sizeof(InfoRequest)); InfoRequest.MessageType = pRequest->MessageType; InfoRequest.LogonId = pRequest->LogonId; InfoRequest.Flags = pRequest->Flags; InfoRequest.ServerName.Length = pRequest->ServerName.Length; InfoRequest.ServerName.MaximumLength = pRequest->ServerName.MaximumLength; InfoRequest.ServerName.Buffer = (PVOID) UlongToPtr(pRequest->ServerName.Buffer); pInfoRequest = &InfoRequest; } else { if (SubmitBufferSize < sizeof(SSL_SESSION_CACHE_INFO_REQUEST)) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; } pInfoRequest = (PSSL_SESSION_CACHE_INFO_REQUEST)ProtocolSubmitBuffer; } // // Normalize the strings // NULL_RELOCATE_ONE(&pInfoRequest->ServerName); // // Find the callers logon id & TCB status // Status = LsaTable->GetClientInfo(&ClientInfo); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // If the caller did not provide a logon id, use the caller's logon id. // if ( RtlIsZeroLuid( &pInfoRequest->LogonId ) ) { LogonId = &ClientInfo.LogonId; } else { // // Verify the caller has TCB privilege if they want access to someone // else's session cache. // if (!ClientInfo.HasTcbPrivilege) { Status = STATUS_PRIVILEGE_NOT_HELD; goto Cleanup; } LogonId = &pInfoRequest->LogonId; } pInfoResponse = SPExternalAlloc(sizeof(SSL_SESSION_CACHE_INFO_RESPONSE)); if(pInfoResponse == NULL) { Status = STATUS_NO_MEMORY; goto Cleanup; } Status = SPCacheGetInfo(LogonId, pInfoRequest->ServerName.Buffer, pInfoRequest->Flags, pInfoResponse); if(!NT_SUCCESS(Status)) { goto Cleanup; } cbInfoResponse = sizeof(SSL_SESSION_CACHE_INFO_RESPONSE); // // Copy the response data to the client process. // Status = LsaTable->AllocateClientBuffer( NULL, cbInfoResponse, &pvClient); if(!NT_SUCCESS(Status)) { goto Cleanup; } Status = LsaTable->CopyToClientBuffer( NULL, cbInfoResponse, pvClient, pInfoResponse); if(!NT_SUCCESS(Status)) { LsaTable->FreeClientBuffer(NULL, pvClient); goto Cleanup; } *ProtocolReturnBuffer = pvClient; *ReturnBufferLength = cbInfoResponse; Cleanup: if(pInfoResponse) { SPExternalFree(pInfoResponse); } *ProtocolStatus = Status; return(STATUS_SUCCESS); } NTSTATUS NTAPI SslGetPerfmonInfo( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferSize, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus) { NTSTATUS Status; PSSL_PERFMON_INFO_REQUEST pInfoRequest; PSSL_PERFMON_INFO_RESPONSE pInfoResponse = NULL; DWORD cbInfoResponse; PVOID pvClient; *ProtocolReturnBuffer = NULL; *ReturnBufferLength = 0; // // Verify the request. // if (SubmitBufferSize < sizeof(SSL_PERFMON_INFO_REQUEST)) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; } pInfoRequest = (PSSL_PERFMON_INFO_REQUEST)ProtocolSubmitBuffer; pInfoResponse = SPExternalAlloc(sizeof(SSL_PERFMON_INFO_RESPONSE)); if(pInfoResponse == NULL) { Status = STATUS_NO_MEMORY; goto Cleanup; } Status = SPCacheGetPerfmonInfo(pInfoRequest->Flags, pInfoResponse); if(!NT_SUCCESS(Status)) { goto Cleanup; } cbInfoResponse = sizeof(SSL_PERFMON_INFO_RESPONSE); // // Copy the response data to the client process. // Status = LsaTable->AllocateClientBuffer( NULL, cbInfoResponse, &pvClient); if(!NT_SUCCESS(Status)) { goto Cleanup; } Status = LsaTable->CopyToClientBuffer( NULL, cbInfoResponse, pvClient, pInfoResponse); if(!NT_SUCCESS(Status)) { LsaTable->FreeClientBuffer(NULL, pvClient); goto Cleanup; } *ProtocolReturnBuffer = pvClient; *ReturnBufferLength = cbInfoResponse; Cleanup: if(pInfoResponse) { SPExternalFree(pInfoResponse); } *ProtocolStatus = Status; return(STATUS_SUCCESS); } NTSTATUS SpCallPackagePassthrough( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { // // NOTE: if other sensitive request types are to be supported, // this routine should filter them out prior to calling SpCallPackage. // This is required because untrusted code has the opportunity for // making genericpassthrough requests. // PULONG Request ; if ( !ProtocolSubmitBuffer ) { return SEC_E_UNSUPPORTED_FUNCTION ; } Request = (PULONG) ProtocolSubmitBuffer ; if ( *Request != SSL_LOOKUP_CERT_MESSAGE ) return SEC_E_UNSUPPORTED_FUNCTION; return SpCallPackage( ClientRequest, ProtocolSubmitBuffer, ClientBufferBase, SubmitBufferLength, ProtocolReturnBuffer, ReturnBufferLength, ProtocolStatus ); } SECURITY_STATUS SEC_ENTRY SpShutdown(void) { return(SEC_E_UNSUPPORTED_FUNCTION); } SECURITY_STATUS SEC_ENTRY SpSystemLogon( PSECURITY_STRING pName, DWORD cbKey, PBYTE pbKey, DWORD * pdwHandle, PTimeStamp ptsExpiry) { return(SEC_E_UNSUPPORTED_FUNCTION); } SECURITY_STATUS SEC_ENTRY SpGetUserInfo( PLUID pLogonId, ULONG fFlags, PSecurityUserData * ppUserInfo) { return(SEC_E_UNSUPPORTED_FUNCTION); } //+--------------------------------------------------------------------------- // // Function: SpSaveCredentials // // Synopsis: Store credentials (not supported) // // Arguments: [dwCredHandle] -- // [CredType] -- // [pCredentials] -- // // // History: 7-26-96 RichardW Created // // Notes: // //---------------------------------------------------------------------------- SECURITY_STATUS SEC_ENTRY SpSaveCredentials( LSA_SEC_HANDLE dwCredHandle, PSecBuffer pCredentials) { return(SEC_E_UNSUPPORTED_FUNCTION); } //+--------------------------------------------------------------------------- // // Function: SpGetCredentials // // Synopsis: Get Credentials (not supported) // // Arguments: [dwCredHandle] -- // [CredType] -- // [pCredentials] -- // // History: 7-26-96 RichardW Created // // Notes: // //---------------------------------------------------------------------------- SECURITY_STATUS SEC_ENTRY SpGetCredentials( LSA_SEC_HANDLE dwCredHandle, PSecBuffer pCredentials) { return(SEC_E_UNSUPPORTED_FUNCTION); } //+--------------------------------------------------------------------------- // // Function: SpDeleteCredentials // // Synopsis: Delete stored creds (not supported) // // Arguments: [dwCredHandle] -- // [CredType] -- // [pKey] -- // // History: 7-26-96 RichardW Created // // Notes: // //---------------------------------------------------------------------------- SECURITY_STATUS SEC_ENTRY SpDeleteCredentials(LSA_SEC_HANDLE dwCredHandle, PSecBuffer pKey) { return(SEC_E_UNSUPPORTED_FUNCTION); }