//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1995. // // File: usermode.c // // Contents: User mode functions // // Classes: // // Functions: // // History: 10-08-96 RichardW Created // //---------------------------------------------------------------------------- #include "sslp.h" #include #include #include #include // Counter for exported handles ULONG_PTR ExportedContext = 0; SECURITY_STATUS UpdateContextUsrToLsa(IN LSA_SEC_HANDLE hContextHandle); SECPKG_USER_FUNCTION_TABLE SslTable[ 2 ] = { { SpInstanceInit, SpInitUserModeContext, SpMakeSignature, SpVerifySignature, SpSealMessage, SpUnsealMessage, SpGetContextToken, SpUserQueryContextAttributes, SpCompleteAuthToken, SpDeleteUserModeContext, SpFormatCredentials, SpMarshallSupplementalCreds, SpExportSecurityContext, SpImportSecurityContext }, { SpInstanceInit, SpInitUserModeContext, SpMakeSignature, SpVerifySignature, SpSealMessage, SpUnsealMessage, SpGetContextToken, SpUserQueryContextAttributes, SpCompleteAuthToken, SpDeleteUserModeContext, SpFormatCredentials, SpMarshallSupplementalCreds, SpExportSecurityContext, SpImportSecurityContext } }; NTSTATUS SEC_ENTRY SpUserModeInitialize( IN ULONG LsaVersion, OUT PULONG PackageVersion, OUT PSECPKG_USER_FUNCTION_TABLE * UserFunctionTable, OUT PULONG pcTables) { if (LsaVersion != SECPKG_INTERFACE_VERSION) { DebugLog((DEB_ERROR,"Invalid LSA version: %d\n", LsaVersion)); return(STATUS_INVALID_PARAMETER); } *PackageVersion = SECPKG_INTERFACE_VERSION ; *UserFunctionTable = &SslTable[0] ; *pcTables = 2; SslInitContextManager(); return( STATUS_SUCCESS ); } NTSTATUS NTAPI SpInstanceInit( IN ULONG Version, IN PSECPKG_DLL_FUNCTIONS DllFunctionTable, OUT PVOID * UserFunctionTable ) { NTSTATUS Status; DWORD i; // Register callback functions. for(i = 0; i < g_cSchannelCallbacks; i++) { Status = DllFunctionTable->RegisterCallback( g_SchannelCallbacks[i].dwTag, g_SchannelCallbacks[i].pFunction); if(Status != STATUS_SUCCESS) { return Status; } } return(STATUS_SUCCESS); } //+------------------------------------------------------------------------- // // Function: SpDeleteUserModeContext // // Synopsis: Deletes a user mode context by unlinking it and then // dereferencing it. // // Effects: // // Arguments: ContextHandle - Lsa context handle of the context to delete // // Requires: // // Returns: STATUS_SUCCESS on success, STATUS_INVALID_HANDLE if the // context can't be located // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpDeleteUserModeContext( IN LSA_SEC_HANDLE ContextHandle ) { SslDeleteUserContext( ContextHandle ); return( SEC_E_OK ); } //+------------------------------------------------------------------------- // // Function: SpInitUserModeContext // // Synopsis: Creates a user-mode context from a packed LSA mode context // // Effects: // // Arguments: ContextHandle - Lsa mode context handle for the context // PackedContext - A marshalled buffer containing the LSA // mode context. // // Requires: // // Returns: STATUS_SUCCESS or STATUS_INSUFFICIENT_RESOURCES // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpInitUserModeContext( IN LSA_SEC_HANDLE ContextHandle, IN PSecBuffer PackedContext ) { SECURITY_STATUS scRet ; if(!SchannelInit(TRUE)) { return SP_LOG_RESULT(SEC_E_INTERNAL_ERROR); } scRet = SslAddUserContext( ContextHandle, NULL, PackedContext, FALSE ); if ( NT_SUCCESS( scRet ) ) { if(g_pFreeContextBuffer) { g_pFreeContextBuffer( PackedContext->pvBuffer ); } } return( scRet ); } //+------------------------------------------------------------------------- // // Function: SpMakeSignature // // Synopsis: Signs a message buffer by calculatinga checksum over all // the non-read only data buffers and encrypting the checksum // along with a nonce. // // Effects: // // Arguments: ContextHandle - Handle of the context to use to sign the // message. // QualityOfProtection - Unused flags. // MessageBuffers - Contains an array of buffers to sign and // to store the signature. // MessageSequenceNumber - Sequence number for this message, // only used in datagram cases. // // Requires: STATUS_INVALID_HANDLE - the context could not be found or // was not configured for message integrity. // STATUS_INVALID_PARAMETER - the signature buffer could not // be found. // STATUS_BUFFER_TOO_SMALL - the signature buffer is too small // to hold the signature // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpMakeSignature( IN LSA_SEC_HANDLE ContextHandle, IN ULONG QualityOfProtection, IN PSecBufferDesc MessageBuffers, IN ULONG MessageSequenceNumber ) { return( SEC_E_UNSUPPORTED_FUNCTION ); } //+------------------------------------------------------------------------- // // Function: SpVerifySignature // // Synopsis: Verifies a signed message buffer by calculating a checksum over all // the non-read only data buffers and encrypting the checksum // along with a nonce. // // Effects: // // Arguments: ContextHandle - Handle of the context to use to sign the // message. // MessageBuffers - Contains an array of signed buffers and // a signature buffer. // MessageSequenceNumber - Sequence number for this message, // only used in datagram cases. // QualityOfProtection - Unused flags. // // Requires: STATUS_INVALID_HANDLE - the context could not be found or // was not configured for message integrity. // STATUS_INVALID_PARAMETER - the signature buffer could not // be found or was too small. // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpVerifySignature( IN LSA_SEC_HANDLE ContextHandle, IN PSecBufferDesc MessageBuffers, IN ULONG MessageSequenceNumber, OUT PULONG QualityOfProtection ) { return( SEC_E_UNSUPPORTED_FUNCTION ); } NTSTATUS NTAPI SpSealMessage( IN LSA_SEC_HANDLE ContextHandle, IN ULONG QualityOfProtection, IN PSecBufferDesc pMessage, IN ULONG MessageSequenceNumber ) { PSSL_USER_CONTEXT Context ; PSPContext pContext; PSecBuffer pHdrBuffer; PSecBuffer pDataBuffer; PSecBuffer pTlrBuffer; PSecBuffer pTokenBuffer; SP_STATUS pctRet = PCT_ERR_OK; SPBuffer CommOut; SPBuffer AppIn; DWORD cbBuffer; BOOL fAlloced = FALSE; BOOL fConnectionMode = FALSE; int i; SP_BEGIN("SpSealMessage"); Context = SslFindUserContext( ContextHandle ); if ( !Context ) { SP_RETURN( SP_LOG_RESULT(SEC_E_INVALID_HANDLE) ); } pContext = Context->pContext; if(pContext == NULL) { SP_RETURN( SP_LOG_RESULT(SEC_E_INVALID_HANDLE) ); } if(!(pContext->State & SP_STATE_CONNECTED) || !pContext->Encrypt) { SP_RETURN( SP_LOG_RESULT(SEC_E_CONTEXT_EXPIRED) ); } // // Find the buffer with the data: // pHdrBuffer = NULL; pDataBuffer = NULL; pTlrBuffer = NULL; pTokenBuffer = NULL; /* Gibraltar passes in the following, * a TOKEN buffer (or SECBUFFER_STREAM_HEADER) * a DATA buffer * a TOKEN buffer (or SECBUFFER_STREAM_TRAILER) * or we can get a connection mode as in * DATA buffer * Token buffer */ if(0 == (pContext->Flags & CONTEXT_FLAG_CONNECTION_MODE)) { // Stream Mode // The output buffer should be a concatenation of // the header buffer, Data buffer, and Trailer buffers. for (i = 0 ; i < (int)pMessage->cBuffers ; i++ ) { switch(pMessage->pBuffers[i].BufferType) { case SECBUFFER_STREAM_HEADER: pHdrBuffer = &pMessage->pBuffers[i]; break; case SECBUFFER_DATA : pDataBuffer = &pMessage->pBuffers[i]; if(pHdrBuffer == NULL) pHdrBuffer = pDataBuffer; break; case SECBUFFER_STREAM_TRAILER: pTlrBuffer = &pMessage->pBuffers[i]; break; case SECBUFFER_TOKEN: if(pHdrBuffer == NULL) { pHdrBuffer = &pMessage->pBuffers[i]; } else if(pTlrBuffer == NULL) { pTlrBuffer = &pMessage->pBuffers[i]; } break; default: break; } } if (!pHdrBuffer || !pDataBuffer ) { SP_RETURN( SP_LOG_RESULT(SEC_E_INVALID_TOKEN) ); } #if DBG DebugLog((DEB_TRACE, "Header (uninitialized): cbBuffer:0x%x, pvBuffer:0x%8.8x\n", pHdrBuffer->cbBuffer, pHdrBuffer->pvBuffer)); DebugLog((DEB_TRACE, "Data (plaintext): cbBuffer:0x%x, pvBuffer:0x%8.8x\n", pDataBuffer->cbBuffer, pDataBuffer->pvBuffer)); DBG_HEX_STRING(DEB_BUFFERS, pDataBuffer->pvBuffer, pDataBuffer->cbBuffer); if(pTlrBuffer) { DebugLog((DEB_TRACE, "Trailer (uninitialized): cbBuffer:0x%x, pvBuffer:0x%8.8x\n", pTlrBuffer->cbBuffer, pTlrBuffer->pvBuffer)); } #endif // Now, figure out if all of the buffers are contiguous, if not, then we // have to allocate a buffer fAlloced = FALSE; if((PUCHAR)pDataBuffer->pvBuffer != ((PUCHAR)pHdrBuffer->pvBuffer + pHdrBuffer->cbBuffer)) { fAlloced = TRUE; } if(pTlrBuffer) { if((PUCHAR)pTlrBuffer->pvBuffer != ((PUCHAR)pDataBuffer->pvBuffer + pDataBuffer->cbBuffer)) { fAlloced = TRUE; } } if(!fAlloced) { // All of our buffers are contiguous, so we do // not need to allocate a contiguous buffer. pTokenBuffer = pHdrBuffer; AppIn.pvBuffer = pDataBuffer->pvBuffer; AppIn.cbData = pDataBuffer->cbBuffer; AppIn.cbBuffer = pDataBuffer->cbBuffer; CommOut.pvBuffer = pHdrBuffer->pvBuffer; CommOut.cbData = 0; CommOut.cbBuffer = pHdrBuffer->cbBuffer + pDataBuffer->cbBuffer; if(pTlrBuffer) { CommOut.cbBuffer += pTlrBuffer->cbBuffer; AppIn.cbBuffer += pTlrBuffer->cbBuffer; } } else { // Our buffers are not contiguous, so we must allocate a contiguous // buffer to do our work in. // Calculate the size of the buffer CommOut.cbBuffer = pHdrBuffer->cbBuffer + pDataBuffer->cbBuffer; if(pTlrBuffer) { CommOut.cbBuffer += pTlrBuffer->cbBuffer; } // Allocate the buffer CommOut.pvBuffer = SPExternalAlloc(CommOut.cbBuffer); if(CommOut.pvBuffer == NULL) { SP_RETURN( SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY) ); } // Copy data to encrypt to the buffer CommOut.cbData = 0; AppIn.pvBuffer = (PBYTE)CommOut.pvBuffer + pHdrBuffer->cbBuffer; AppIn.cbBuffer = CommOut.cbBuffer - pHdrBuffer->cbBuffer; AppIn.cbData = pDataBuffer->cbBuffer; CopyMemory((PBYTE)AppIn.pvBuffer, (PBYTE)pDataBuffer->pvBuffer, pDataBuffer->cbBuffer); } pctRet = pContext->Encrypt(pContext, &AppIn, &CommOut); if(pctRet == PCT_ERR_OK) { // Set the various buffer sizes. cbBuffer = CommOut.cbData; // The first few bytes always go in the header buffer. pHdrBuffer->cbBuffer = min(cbBuffer, pHdrBuffer->cbBuffer); cbBuffer -= pHdrBuffer->cbBuffer; if(pTlrBuffer) { // The output data buffer is the same size as the input data buffer. pDataBuffer->cbBuffer = min(cbBuffer, pDataBuffer->cbBuffer); cbBuffer -= pDataBuffer->cbBuffer; // The trailer buffer gets the data that's left over. pTlrBuffer->cbBuffer = min(cbBuffer, pTlrBuffer->cbBuffer); cbBuffer -= pTlrBuffer->cbBuffer; } else { pDataBuffer->cbBuffer = min(cbBuffer, pDataBuffer->cbBuffer); cbBuffer -= pDataBuffer->cbBuffer; } if(fAlloced) { // If we allocated the buffer, then we must copy CopyMemory(pHdrBuffer->pvBuffer, CommOut.pvBuffer, pHdrBuffer->cbBuffer); CopyMemory(pDataBuffer->pvBuffer, (PUCHAR)CommOut.pvBuffer + pHdrBuffer->cbBuffer, pDataBuffer->cbBuffer); if(pTlrBuffer) { CopyMemory(pTlrBuffer->pvBuffer, (PUCHAR)CommOut.pvBuffer + pHdrBuffer->cbBuffer + pDataBuffer->cbBuffer, pTlrBuffer->cbBuffer); } } #if DBG DebugLog((DEB_TRACE, "Header (ciphertext): cbBuffer:0x%x, pvBuffer:0x%8.8x\n", pHdrBuffer->cbBuffer, pHdrBuffer->pvBuffer)); DBG_HEX_STRING(DEB_BUFFERS, pHdrBuffer->pvBuffer, pHdrBuffer->cbBuffer); DebugLog((DEB_TRACE, "Data (ciphertext): cbBuffer:0x%x, pvBuffer:0x%8.8x\n", pDataBuffer->cbBuffer, pDataBuffer->pvBuffer)); DBG_HEX_STRING(DEB_BUFFERS, pDataBuffer->pvBuffer, pDataBuffer->cbBuffer); if(pTlrBuffer) { DebugLog((DEB_TRACE, "Trailer (ciphertext): cbBuffer:0x%x, pvBuffer:0x%8.8x\n", pTlrBuffer->cbBuffer, pTlrBuffer->pvBuffer)); DBG_HEX_STRING(DEB_BUFFERS, pTlrBuffer->pvBuffer, pTlrBuffer->cbBuffer); } #endif } } else { // We're doing connection mode, so unpack buffers as a // Data and then Token buffer fConnectionMode = TRUE; for (i = 0 ; i < (int)pMessage->cBuffers ; i++ ) { switch(pMessage->pBuffers[i].BufferType) { case SECBUFFER_DATA : pDataBuffer = &pMessage->pBuffers[i]; break; case SECBUFFER_TOKEN: if(pTokenBuffer == NULL) { pTokenBuffer = &pMessage->pBuffers[i]; } break; default: break; } } if((pTokenBuffer == NULL) || (pDataBuffer == NULL)) { SP_RETURN( SP_LOG_RESULT(SEC_E_INVALID_TOKEN) ); } if((pDataBuffer->pvBuffer == NULL) || (pTokenBuffer->pvBuffer == NULL)) { SP_RETURN( SP_LOG_RESULT(SEC_E_INVALID_TOKEN) ); } #if DBG DebugLog((DEB_TRACE, "Data (plaintext): cbBuffer:0x%x, pvBuffer:0x%8.8x\n", pDataBuffer->cbBuffer, pDataBuffer->pvBuffer)); DBG_HEX_STRING(DEB_BUFFERS, pDataBuffer->pvBuffer, pDataBuffer->cbBuffer); DebugLog((DEB_TRACE, "Token (uninitialized): cbBuffer:0x%x, pvBuffer:0x%8.8x\n", pTokenBuffer->cbBuffer, pTokenBuffer->pvBuffer)); #endif // Connection Mode // The output should get written to a concatenation of the // data buffer and the token buffer. If no token buffer is // given, then we should allocate one. if((PUCHAR)pTokenBuffer->pvBuffer == ((PUCHAR)pDataBuffer->pvBuffer + pDataBuffer->cbBuffer)) { // If the buffers are contiguous, we can optimize! CommOut.pvBuffer = pDataBuffer->pvBuffer; CommOut.cbData = 0; CommOut.cbBuffer = pDataBuffer->cbBuffer + pTokenBuffer->cbBuffer; } else { // We have to realloc the buffer fAlloced = TRUE; CommOut.pvBuffer = SPExternalAlloc(pDataBuffer->cbBuffer + pTokenBuffer->cbBuffer); if(CommOut.pvBuffer == NULL) { SP_RETURN( SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY) ); } CommOut.cbBuffer = pDataBuffer->cbBuffer + pTokenBuffer->cbBuffer; CommOut.cbData = 0; } // The data buffer always goes to AppIn AppIn.pvBuffer = pDataBuffer->pvBuffer; AppIn.cbData = pDataBuffer->cbBuffer; AppIn.cbBuffer = pDataBuffer->cbBuffer; pctRet = pContext->Encrypt(pContext, &AppIn, &CommOut); if(pctRet == PCT_ERR_OK) { // Set the various buffer sizes. cbBuffer = CommOut.cbData; // The first few bytes always go into the data buffer. pDataBuffer->cbBuffer = min(cbBuffer, pDataBuffer->cbBuffer); cbBuffer -= pDataBuffer->cbBuffer; // The remaining bytes go into the token buffer. pTokenBuffer->cbBuffer = min(cbBuffer, pTokenBuffer->cbBuffer); if(fAlloced) { // We encrypted into our temporary buffer, so we must // copy. CopyMemory(pDataBuffer->pvBuffer, CommOut.pvBuffer, pDataBuffer->cbBuffer); CopyMemory(pTokenBuffer->pvBuffer, (PUCHAR)CommOut.pvBuffer + pDataBuffer->cbBuffer, pTokenBuffer->cbBuffer); } } #if DBG DebugLog((DEB_TRACE, "Data (ciphertext): cbBuffer:0x%x, pvBuffer:0x%8.8x\n", pDataBuffer->cbBuffer, pDataBuffer->pvBuffer)); DBG_HEX_STRING(DEB_BUFFERS, pDataBuffer->pvBuffer, pDataBuffer->cbBuffer); DebugLog((DEB_TRACE, "Token (ciphertext): cbBuffer:0x%x, pvBuffer:0x%8.8x\n", pTokenBuffer->cbBuffer, pTokenBuffer->pvBuffer)); DBG_HEX_STRING(DEB_BUFFERS, pTokenBuffer->pvBuffer, pTokenBuffer->cbBuffer); #endif } if(fAlloced) { SPExternalFree(CommOut.pvBuffer); } SP_RETURN( PctTranslateError(pctRet) ); } NTSTATUS NTAPI SpUnsealMessage( IN LSA_SEC_HANDLE ContextHandle, IN PSecBufferDesc pMessage, IN ULONG MessageSequenceNumber, OUT PULONG QualityOfProtection ) { // Output Buffer Types PSSL_USER_CONTEXT Context ; PSecBuffer pHdrBuffer; PSecBuffer pDataBuffer; PSecBuffer pTokenBuffer; PSecBuffer pTlrBuffer; PSecBuffer pExtraBuffer; SP_STATUS pctRet = PCT_ERR_OK; SPBuffer CommIn; SPBuffer AppOut; PSPContext pContext; DWORD cbHeaderSize; BOOL fAlloced = FALSE; SECURITY_STATUS scRet; int i; SP_BEGIN("SpUnsealMessage"); Context = SslFindUserContext( ContextHandle ); if ( !Context ) { SP_RETURN( SP_LOG_RESULT(SEC_E_INVALID_HANDLE) ); } pContext = Context->pContext; if(pContext == NULL) { SP_RETURN( SP_LOG_RESULT(SEC_E_INVALID_HANDLE) ); } if(!(pContext->State & SP_STATE_CONNECTED)) { SP_RETURN( SP_LOG_RESULT(SEC_E_CONTEXT_EXPIRED) ); } // // Set up output buffers: // pHdrBuffer = NULL; pDataBuffer = NULL; pTokenBuffer = NULL; pTlrBuffer = NULL; pExtraBuffer = NULL; // On input, the buffers can either be // DataBuffer // TokenBuffer // // or // // Data Buffer // Empty // Empty // Empty // // on Output, the buffers are // DataBuffer // TokenBuffer // // or // HdrBuffer // DataBuffer // Tlrbuffer // Extrabuffer or Empty if(0 == (pContext->Flags & CONTEXT_FLAG_CONNECTION_MODE)) { // Stream Mode // The output buffer should be a concatenation of // the header buffer, Data buffer, and Trailer buffers. for (i = 0 ; i < (int)pMessage->cBuffers ; i++ ) { switch(pMessage->pBuffers[i].BufferType) { case SECBUFFER_DATA: // The message data buffer on input will be the hdr buffer on // output. pHdrBuffer = &pMessage->pBuffers[i]; break; case SECBUFFER_EMPTY: if(pDataBuffer == NULL) { pDataBuffer = &pMessage->pBuffers[i]; } else if (pTlrBuffer == NULL) { pTlrBuffer = &pMessage->pBuffers[i]; } else if (pExtraBuffer == NULL) { pExtraBuffer = &pMessage->pBuffers[i]; } break; default: break; } } if(!pHdrBuffer || !pDataBuffer || !pTlrBuffer || !pExtraBuffer) { SP_RETURN( SP_LOG_RESULT(SEC_E_INVALID_TOKEN) ); } if(pHdrBuffer->pvBuffer == NULL) { SP_RETURN( SP_LOG_RESULT(SEC_E_INVALID_TOKEN) ); } #if DBG DebugLog((DEB_TRACE, "Data (ciphertext): cbBuffer:0x%x, pvBuffer:0x%8.8x\n", pHdrBuffer->cbBuffer, pHdrBuffer->pvBuffer)); DBG_HEX_STRING(DEB_BUFFERS, pHdrBuffer->pvBuffer, pHdrBuffer->cbBuffer); #endif CommIn.pvBuffer = pHdrBuffer->pvBuffer; CommIn.cbData = pHdrBuffer->cbBuffer; CommIn.cbBuffer = pHdrBuffer->cbBuffer; pctRet = pContext->GetHeaderSize(pContext, &CommIn, &cbHeaderSize); if(pctRet == PCT_ERR_OK) { AppOut.pvBuffer = (PUCHAR)CommIn.pvBuffer + cbHeaderSize; AppOut.cbData = 0; AppOut.cbBuffer = CommIn.cbData-cbHeaderSize; pctRet = pContext->DecryptHandler(pContext, &CommIn, &AppOut); } if((pctRet == PCT_ERR_OK) || (pctRet == PCT_INT_RENEGOTIATE)) { if(CommIn.cbData < pHdrBuffer->cbBuffer) { pExtraBuffer->BufferType = SECBUFFER_EXTRA; pExtraBuffer->cbBuffer = pHdrBuffer->cbBuffer-CommIn.cbData; pExtraBuffer->pvBuffer = (PUCHAR)pHdrBuffer->pvBuffer+CommIn.cbData; } else { pExtraBuffer = NULL; } pHdrBuffer->BufferType = SECBUFFER_STREAM_HEADER; pHdrBuffer->cbBuffer = cbHeaderSize; pDataBuffer->BufferType = SECBUFFER_DATA; pDataBuffer->pvBuffer = AppOut.pvBuffer; pDataBuffer->cbBuffer = AppOut.cbData; pTlrBuffer->BufferType = SECBUFFER_STREAM_TRAILER; pTlrBuffer->pvBuffer = (PUCHAR)pDataBuffer->pvBuffer + AppOut.cbData; pTlrBuffer->cbBuffer = CommIn.cbBuffer-(AppOut.cbData+cbHeaderSize); #if DBG DebugLog((DEB_TRACE, "Header (plaintext): cbBuffer:0x%x, pvBuffer:0x%8.8x\n", pHdrBuffer->cbBuffer, pHdrBuffer->pvBuffer)); DBG_HEX_STRING(DEB_BUFFERS, pHdrBuffer->pvBuffer, pHdrBuffer->cbBuffer); DebugLog((DEB_TRACE, "Data (plaintext): cbBuffer:0x%x, pvBuffer:0x%8.8x\n", pDataBuffer->cbBuffer, pDataBuffer->pvBuffer)); DBG_HEX_STRING(DEB_BUFFERS, pDataBuffer->pvBuffer, pDataBuffer->cbBuffer); DebugLog((DEB_TRACE, "Trailer (plaintext): cbBuffer:0x%x, pvBuffer:0x%8.8x\n", pTlrBuffer->cbBuffer, pTlrBuffer->pvBuffer)); DBG_HEX_STRING(DEB_BUFFERS, pTlrBuffer->pvBuffer, pTlrBuffer->cbBuffer); if(pExtraBuffer) { DebugLog((DEB_TRACE, "Extra (ciphertext): cbBuffer:0x%x, pvBuffer:0x%8.8x\n", pExtraBuffer->cbBuffer, pExtraBuffer->pvBuffer)); DBG_HEX_STRING(DEB_BUFFERS, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer); } #endif if(pctRet == PCT_INT_RENEGOTIATE) { // Wow. Need to notify the lsa mode portion of the context that // the caller is about to call AcceptSecurityContext again, and // not to panic. So, we cruft up a magic "token" that we // pass along in ApplyControlToken: scRet = UpdateContextUsrToLsa(ContextHandle); if(FAILED(scRet)) { SP_RETURN( SP_LOG_RESULT(scRet) ); } } } else if(pctRet == PCT_INT_INCOMPLETE_MSG) { pDataBuffer->BufferType = SECBUFFER_MISSING; pDataBuffer->cbBuffer = CommIn.cbData - pHdrBuffer->cbBuffer; /* This is a hack to work with old code that was designed to work with * the old SSL. */ pHdrBuffer->BufferType = SECBUFFER_MISSING; pHdrBuffer->cbBuffer = CommIn.cbData - pHdrBuffer->cbBuffer; } } else { // Connection Mode for (i = 0 ; i < (int)pMessage->cBuffers ; i++ ) { switch(pMessage->pBuffers[i].BufferType) { case SECBUFFER_DATA : pDataBuffer = &pMessage->pBuffers[i]; break; case SECBUFFER_TOKEN: if(pTokenBuffer == NULL) { pTokenBuffer = &pMessage->pBuffers[i]; } break; default: break; } } if((pTokenBuffer == NULL) || (pDataBuffer == NULL)) { SP_RETURN( SP_LOG_RESULT(SEC_E_INVALID_TOKEN) ); } if((pDataBuffer->pvBuffer == NULL) || (pTokenBuffer->pvBuffer == NULL)) { SP_RETURN( SP_LOG_RESULT(SEC_E_INVALID_TOKEN) ); } #if DBG DebugLog((DEB_TRACE, "Data (ciphertext): cbBuffer:0x%x, pvBuffer:0x%8.8x\n", pDataBuffer->cbBuffer, pDataBuffer->pvBuffer)); DBG_HEX_STRING(DEB_BUFFERS, pDataBuffer->pvBuffer, pDataBuffer->cbBuffer); DebugLog((DEB_TRACE, "Token (ciphertext): cbBuffer:0x%x, pvBuffer:0x%8.8x\n", pTokenBuffer->cbBuffer, pTokenBuffer->pvBuffer)); DBG_HEX_STRING(DEB_BUFFERS, pTokenBuffer->pvBuffer, pTokenBuffer->cbBuffer); #endif // The Data and Token buffers are concatenated together to // form a single input buffer. if((PUCHAR)pDataBuffer->pvBuffer + pDataBuffer->cbBuffer == (PUCHAR)pTokenBuffer->pvBuffer) { // Speed Opt, If the buffers really are just one big buffer // then we can party on them directly. CommIn.pvBuffer = pDataBuffer->pvBuffer; CommIn.cbData = pDataBuffer->cbBuffer + pTokenBuffer->cbBuffer; CommIn.cbBuffer = CommIn.cbData; } else { // We have to allocate a uniform input buffer CommIn.cbData = pDataBuffer->cbBuffer + pTokenBuffer->cbBuffer; CommIn.pvBuffer = SPExternalAlloc(CommIn.cbData); if(CommIn.pvBuffer == NULL) { SP_RETURN( SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY) ); } CommIn.cbBuffer = CommIn.cbData; CopyMemory(CommIn.pvBuffer, pDataBuffer->pvBuffer, pDataBuffer->cbBuffer); CopyMemory((PUCHAR)CommIn.pvBuffer + pDataBuffer->cbBuffer, pTokenBuffer->pvBuffer, pTokenBuffer->cbBuffer); fAlloced = TRUE; } AppOut.pvBuffer = pDataBuffer->pvBuffer; AppOut.cbData = 0; AppOut.cbBuffer = pDataBuffer->cbBuffer; pctRet = pContext->DecryptHandler(pContext, &CommIn, &AppOut); if((pctRet == PCT_ERR_OK) || (pctRet == PCT_INT_RENEGOTIATE)) { pDataBuffer->cbBuffer = AppOut.cbData; pTokenBuffer->cbBuffer = CommIn.cbData - AppOut.cbData; } if(fAlloced) { SPExternalFree(CommIn.pvBuffer); } #if DBG DebugLog((DEB_TRACE, "Data (plaintext): cbBuffer:0x%x, pvBuffer:0x%8.8x\n", pDataBuffer->cbBuffer, pDataBuffer->pvBuffer)); DBG_HEX_STRING(DEB_BUFFERS, pDataBuffer->pvBuffer, pDataBuffer->cbBuffer); DebugLog((DEB_TRACE, "Token (plaintext): cbBuffer:0x%x, pvBuffer:0x%8.8x\n", pTokenBuffer->cbBuffer, pTokenBuffer->pvBuffer)); DBG_HEX_STRING(DEB_BUFFERS, pTokenBuffer->pvBuffer, pTokenBuffer->cbBuffer); #endif } DebugOut(( DEB_TRACE, "Unseal returns %x \n", PctTranslateError( pctRet ) )); SP_RETURN( PctTranslateError(pctRet) ); } //+------------------------------------------------------------------------- // // Function: SpGetContextToken // // Synopsis: returns a pointer to the token for a server-side context // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpGetContextToken( IN LSA_SEC_HANDLE ContextHandle, OUT PHANDLE ImpersonationToken ) { PSSL_USER_CONTEXT Context; PSPContext pContext; PSessCacheItem pZombie; SECURITY_STATUS Status; Context = SslFindUserContext( ContextHandle ); if ( !Context ) { return( SEC_E_INVALID_HANDLE ); } pContext = Context->pContext; if(pContext == NULL) { SP_RETURN( SP_LOG_RESULT(SEC_E_INVALID_HANDLE) ); } pZombie = pContext->RipeZombie; if(pZombie == NULL || pZombie->hLocator == 0) { if(pZombie->LocatorStatus) { return(SP_LOG_RESULT(pZombie->LocatorStatus)); } else { return(SP_LOG_RESULT(SEC_E_NO_IMPERSONATION)); } } if(pZombie->phMapper == NULL) { *ImpersonationToken = (HANDLE)pZombie->hLocator; } else { // Call the application mapper to get a token. Status = pZombie->phMapper->m_vtable->GetAccessToken( pZombie->phMapper->m_Reserved1, pZombie->hLocator, ImpersonationToken); if(!NT_SUCCESS(Status)) { return SP_LOG_RESULT(Status); } } return( SEC_E_OK ); } //+------------------------------------------------------------------------- // // Function: SpCompleteAuthToken // // Synopsis: Completes a context (in Kerberos case, does nothing) // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpCompleteAuthToken( IN LSA_SEC_HANDLE ContextHandle, IN PSecBufferDesc InputBuffer ) { return(STATUS_SUCCESS); } NTSTATUS NTAPI SpFormatCredentials( IN PSecBuffer Credentials, OUT PSecBuffer FormattedCredentials ) { return(STATUS_NOT_SUPPORTED); } NTSTATUS NTAPI SpMarshallSupplementalCreds( IN ULONG CredentialSize, IN PUCHAR Credentials, OUT PULONG MarshalledCredSize, OUT PVOID * MarshalledCreds ) { return(STATUS_NOT_SUPPORTED); } //+--------------------------------------------------------------------------- // // Function: UpdateContextUsrToLsa // // Synopsis: UnsealMessage has just received a redo request, so push the // read key over to the LSA process. // // Arguments: [hLsaContext] -- Handle to LSA schannel context. // // History: 10-20-97 jbanes Added CAPI integration. // // Notes: The format of the buffer sent to ApplyControlToken is: // // DWORD dwOperation; // SCHANNEL_RENEGOTIATE // DWORD dwNewState; // DWORD dwReadSequence; // DWORD cbReadKey; // BYTE rgbReadKey[]; // //---------------------------------------------------------------------------- SECURITY_STATUS UpdateContextUsrToLsa( IN LSA_SEC_HANDLE hLsaContext) { PSSL_USER_CONTEXT pUserContext; PSPContext pContext; CtxtHandle hMyContext; SecBuffer RedoNotify; SecBufferDesc RedoDesc; SECURITY_STATUS scRet; PBYTE pbBuffer; DWORD cbBuffer; pUserContext = SslFindUserContext( hLsaContext ); if ( !pUserContext ) { return SEC_E_INVALID_HANDLE; } pContext = pUserContext->pContext; if(pContext == NULL) { SP_RETURN( SP_LOG_RESULT(SEC_E_INVALID_HANDLE) ); } hMyContext.dwLower = (DWORD_PTR) GetCurrentThread() ; hMyContext.dwUpper = hLsaContext ; // // Compute size of output buffer. // cbBuffer = sizeof(DWORD) * 2; // // Allocate memory for output buffer. // pbBuffer = SPExternalAlloc( cbBuffer); if(pbBuffer == NULL) { return SEC_E_INSUFFICIENT_MEMORY; } RedoNotify.BufferType = SECBUFFER_TOKEN; RedoNotify.cbBuffer = cbBuffer; RedoNotify.pvBuffer = pbBuffer; RedoDesc.ulVersion = SECBUFFER_VERSION ; RedoDesc.pBuffers = &RedoNotify ; RedoDesc.cBuffers = 1 ; // // Build output buffer. // *(PDWORD)pbBuffer = SCHANNEL_RENEGOTIATE; pbBuffer += sizeof(DWORD); *(PDWORD)pbBuffer = pContext->State; pbBuffer += sizeof(DWORD); // // Call ApplyControlToken // DebugOut(( DEB_TRACE, "Sending state change to LSA since we're renegotiating\n" )); scRet = ApplyControlToken( &hMyContext, &RedoDesc ); LocalFree(RedoNotify.pvBuffer); return scRet; } BOOL SslEmptyCacheA(LPSTR pszTargetName, DWORD dwFlags) { ANSI_STRING String; UNICODE_STRING UnicodeString; NTSTATUS Status; BOOL fSuccess; UnicodeString.Length = 0; UnicodeString.MaximumLength = 0; UnicodeString.Buffer = NULL; // Convert target name to unicode. if(pszTargetName) { RtlInitAnsiString(&String, pszTargetName); Status = RtlAnsiStringToUnicodeString(&UnicodeString, &String, TRUE); if(!NT_SUCCESS(Status)) { SetLastError(SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY)); return FALSE; } } // Call unicode version of function. fSuccess = SslEmptyCacheW(UnicodeString.Buffer, dwFlags); if(UnicodeString.Buffer) { RtlFreeUnicodeString(&UnicodeString); } return fSuccess; } BOOL SslEmptyCacheW(LPWSTR pszTargetName, DWORD dwFlags) { HANDLE LsaHandle = 0; DWORD PackageNumber; LSA_STRING PackageName; PSSL_PURGE_SESSION_CACHE_REQUEST pRequest = NULL; DWORD cbTargetName; DWORD cbRequest; NTSTATUS Status; NTSTATUS SubStatus; Status = LsaConnectUntrusted(&LsaHandle); if(FAILED(Status)) { SP_LOG_RESULT(Status); goto cleanup; } RtlInitAnsiString(&PackageName, SCHANNEL_NAME_A); Status = LsaLookupAuthenticationPackage( LsaHandle, &PackageName, &PackageNumber); if(FAILED(Status)) { SP_LOG_RESULT(Status); goto cleanup; } cbRequest = sizeof(SSL_PURGE_SESSION_CACHE_REQUEST); if(pszTargetName == NULL) { pRequest = SPExternalAlloc(cbRequest); if(pRequest == NULL) { Status = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); goto cleanup; } } else { cbTargetName = (wcslen(pszTargetName) + 1) * sizeof(WCHAR); cbRequest += cbTargetName; pRequest = SPExternalAlloc(cbRequest); if(pRequest == NULL) { Status = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); goto cleanup; } memcpy(pRequest + 1, pszTargetName, cbTargetName); pRequest->ServerName.Buffer = (LPWSTR)(pRequest + 1); pRequest->ServerName.Length = (WORD)(wcslen(pszTargetName) * sizeof(WCHAR)); pRequest->ServerName.MaximumLength = (WORD)cbTargetName; } pRequest->MessageType = SSL_PURGE_CACHE_MESSAGE; pRequest->Flags = SSL_PURGE_CLIENT_ENTRIES | SSL_PURGE_SERVER_ENTRIES; Status = LsaCallAuthenticationPackage( LsaHandle, PackageNumber, pRequest, cbRequest, NULL, NULL, &SubStatus); if(FAILED(Status)) { SP_LOG_RESULT(Status); goto cleanup; } if(FAILED(SubStatus)) { Status = SP_LOG_RESULT(SubStatus); } cleanup: if(LsaHandle) { CloseHandle(LsaHandle); } if(pRequest) { SPExternalFree(pRequest); } if(FAILED(Status)) { SetLastError(Status); return FALSE; } return TRUE; } //+------------------------------------------------------------------------- // // Function: SpExportSecurityContext // // Synopsis: Exports a security context to another process // // Effects: Allocates memory for output // // Arguments: ContextHandle - handle to context to export // Flags - Flags concerning duplication. Allowable flags: // SECPKG_CONTEXT_EXPORT_DELETE_OLD - causes old context // to be deleted. // PackedContext - Receives serialized context to be freed with // FreeContextBuffer // TokenHandle - Optionally receives handle to context's token. // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpExportSecurityContext( LSA_SEC_HANDLE ContextHandle, // (in) context to export ULONG fFlags, // (in) option flags PSecBuffer pPackedContext, // (out) marshalled context PHANDLE pToken // (out, optional) token handle for impersonation ) { PSSL_USER_CONTEXT Context; PSPContext pContext; NTSTATUS Status; SP_STATUS pctRet; DebugLog((DEB_TRACE, "SpExportSecurityContext\n")); if (ARGUMENT_PRESENT(pToken)) { *pToken = NULL; } pPackedContext->pvBuffer = NULL; pPackedContext->cbBuffer = 0; pPackedContext->BufferType = 0; // // Get handle to schannel context structure. // Context = SslFindUserContext( ContextHandle ); if ( !Context ) { Status = SP_LOG_RESULT(SEC_E_INVALID_HANDLE); goto cleanup; } pContext = Context->pContext; if(pContext == NULL) { Status = SP_LOG_RESULT(SEC_E_INVALID_HANDLE); goto cleanup; } if(!(pContext->State & SP_STATE_CONNECTED)) { Status = SP_LOG_RESULT(SEC_E_CONTEXT_EXPIRED); goto cleanup; } // // Build packed context structure. // pctRet = SPContextSerialize(pContext, NULL, (PBYTE *)&pPackedContext->pvBuffer, &pPackedContext->cbBuffer, FALSE); if(pctRet != PCT_ERR_OK) { Status = SP_LOG_RESULT(SEC_E_ENCRYPT_FAILURE); goto cleanup; } // // Now either duplicate the token or copy it. // if (ARGUMENT_PRESENT(pToken) && (pContext->RipeZombie->hLocator)) { if ((fFlags & SECPKG_CONTEXT_EXPORT_DELETE_OLD) != 0) { *pToken = (HANDLE)pContext->RipeZombie->hLocator; pContext->RipeZombie->hLocator = 0; } else { Status = NtDuplicateObject( NtCurrentProcess(), (HANDLE)pContext->RipeZombie->hLocator, NULL, pToken, 0, // no new access 0, // no handle attributes DUPLICATE_SAME_ACCESS ); if (!NT_SUCCESS(Status)) { goto cleanup; } } } Status = STATUS_SUCCESS; cleanup: DebugLog((DEB_TRACE, "SpExportSecurityContext returned 0x%x\n", Status)); return(Status); } NTSTATUS NTAPI SpImportSecurityContext( PSecBuffer pPackedContext, // (in) marshalled context HANDLE Token, // (in, optional) handle to token for context PLSA_SEC_HANDLE ContextHandle // (out) new context handle ) { PSSL_USER_CONTEXT Context = NULL; LSA_SEC_HANDLE LsaHandle; NTSTATUS Status; // Dummy up an lsa handle by incrementing a global variable. This // will ensure that each imported context has a unique handle. // Skip over values that could be interpreted as an aligned pointer, // so that they won't get mixed up with real lsa handles. LsaHandle = InterlockedIncrement((PLONG)&ExportedContext); while(LsaHandle % MAX_NATURAL_ALIGNMENT == 0) { LsaHandle = InterlockedIncrement((PLONG)&ExportedContext); } Status = SslAddUserContext(LsaHandle, Token, pPackedContext, TRUE); if(!NT_SUCCESS(Status)) { return Status; } *ContextHandle = LsaHandle; return(Status); }