windows-nt/Source/XPSP1/NT/ds/security/protocols/schannel/lsa/usermode.c
2020-09-26 16:20:57 +08:00

1559 lines
43 KiB
C

//+---------------------------------------------------------------------------
//
// 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 <ssl2msg.h>
#include <ssl3msg.h>
#include <pct1msg.h>
#include <mapper.h>
// 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);
}