windows-nt/Source/XPSP1/NT/ds/security/protocols/schannel/lsa/callback.c

1634 lines
43 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1995.
//
// File: callback.c
//
// Contents:
//
// Classes:
//
// Functions:
//
// History: 09-23-97 jbanes Created
//
//----------------------------------------------------------------------------
#include "sslp.h"
SECURITY_STATUS
NTAPI
SPSignatureCallback(
ULONG_PTR hProv,
ULONG_PTR aiHash,
SecBuffer *pInput,
SecBuffer *pOutput);
SECURITY_STATUS
NTAPI
UploadCertContextCallback(
ULONG_PTR Argument1,
ULONG_PTR Argument2,
SecBuffer *pInput,
SecBuffer *pOutput);
SECURITY_STATUS
NTAPI
UploadCertStoreCallback(
ULONG_PTR Argument1,
ULONG_PTR Argument2,
SecBuffer *pInput,
SecBuffer *pOutput);
SECURITY_STATUS
NTAPI
RemoteCryptAcquireContextCallback(
ULONG_PTR dwProvType,
ULONG_PTR dwFlags,
SecBuffer *pInput,
SecBuffer *pOutput);
SECURITY_STATUS
RemoteCryptReleaseContextCallback(
ULONG_PTR hProv,
ULONG_PTR dwFlags,
SecBuffer *pInput,
SecBuffer *pOutput);
SECURITY_STATUS
DownloadCertContextCallback(
ULONG_PTR Argument1,
ULONG_PTR Argument2,
SecBuffer *pInput,
SecBuffer *pOutput);
SECURITY_STATUS
NTAPI
GetUserKeysCallback(
ULONG_PTR dwLsaContext,
ULONG_PTR dwFlags,
SecBuffer *pInput,
SecBuffer *pOutput);
SCH_CALLBACK_LIST g_SchannelCallbacks[] =
{
{ SCH_SIGNATURE_CALLBACK, SPSignatureCallback },
{ SCH_UPLOAD_CREDENTIAL_CALLBACK, UploadCertContextCallback },
{ SCH_UPLOAD_CERT_STORE_CALLBACK, UploadCertStoreCallback },
{ SCH_ACQUIRE_CONTEXT_CALLBACK, RemoteCryptAcquireContextCallback },
{ SCH_RELEASE_CONTEXT_CALLBACK, RemoteCryptReleaseContextCallback },
{ SCH_DOWNLOAD_CERT_CALLBACK, DownloadCertContextCallback },
{ SCH_GET_USER_KEYS, GetUserKeysCallback },
{ SCH_REFERENCE_MAPPER_CALLBACK, ReferenceMapperCallback },
{ SCH_GET_MAPPER_ISSUER_LIST_CALLBACK, GetMapperIssuerListCallback },
{ SCH_MAP_CREDENTIAL_CALLBACK, MapCredentialCallback },
{ SCH_CLOSE_LOCATOR_CALLBACK, CloseLocatorCallback },
{ SCH_GET_MAPPER_ATTRIBUTES_CALLBACK, QueryMappedCredAttributesCallback }
};
DWORD g_cSchannelCallbacks = sizeof(g_SchannelCallbacks) / sizeof(SCH_CALLBACK_LIST);
//+---------------------------------------------------------------------------
//
// Function: PerformApplicationCallback
//
// Synopsis: Call back to the application process.
//
// Arguments: [dwCallback] -- Callback function number.
// [dwArg1] --
// [dwArg2] --
// [pInput] --
// [pOutput] --
//
// History: 09-23-97 jbanes Created
//
// Notes:
//
//----------------------------------------------------------------------------
SECURITY_STATUS
PerformApplicationCallback(
DWORD dwCallback,
ULONG_PTR dwArg1,
ULONG_PTR dwArg2,
SecBuffer *pInput,
SecBuffer *pOutput,
BOOL fExpectOutput)
{
SECURITY_STATUS Status;
PVOID pvBuffer;
if(LsaTable == NULL)
{
return SP_LOG_RESULT(SEC_E_INTERNAL_ERROR);
}
pOutput->BufferType = SECBUFFER_EMPTY;
pOutput->pvBuffer = NULL;
pOutput->cbBuffer = 0;
try
{
Status = LsaTable->ClientCallback((PCHAR)ULongToPtr(dwCallback), // Sundown: dwCallback is a function number.
dwArg1,
dwArg2,
pInput,
pOutput);
}
except(EXCEPTION_EXECUTE_HANDLER)
{
Status = SP_LOG_RESULT(SEC_E_UNSUPPORTED_FUNCTION);
}
if ( !NT_SUCCESS( Status ) )
{
return SP_LOG_RESULT( Status );
}
if(Status != SEC_E_OK)
{
SP_LOG_RESULT( Status );
return SEC_E_INTERNAL_ERROR;
}
if(pOutput->pvBuffer && pOutput->cbBuffer)
{
pvBuffer = SPExternalAlloc(pOutput->cbBuffer);
if(pvBuffer == NULL)
{
return SP_LOG_RESULT( SEC_E_INSUFFICIENT_MEMORY );
}
Status = LsaTable->CopyFromClientBuffer(NULL,
pOutput->cbBuffer,
pvBuffer,
pOutput->pvBuffer );
if ( !NT_SUCCESS( Status ) )
{
SPExternalFree(pvBuffer);
return SP_LOG_RESULT( Status );
}
Status = SPFreeUserAllocMemory(pOutput->pvBuffer, pOutput->cbBuffer);
if ( !NT_SUCCESS( Status ) )
{
SPExternalFree(pvBuffer);
return SP_LOG_RESULT( Status );
}
pOutput->pvBuffer = pvBuffer;
}
else if(fExpectOutput)
{
return SP_LOG_RESULT(SEC_E_INTERNAL_ERROR);
}
return Status;
}
// This helper function is called by the LSA process in order to duplicate
// a handle belonging to the application process.
BOOL
DuplicateApplicationHandle(
HANDLE hAppHandle,
LPHANDLE phLsaHandle)
{
SECPKG_CALL_INFO CallInfo;
HANDLE hAppProcess;
HANDLE hLsaProcess;
BOOL fResult;
// Get handle to application process.
if(!LsaTable->GetCallInfo(&CallInfo))
{
return FALSE;
}
hAppProcess = OpenProcess(PROCESS_DUP_HANDLE,
FALSE,
CallInfo.ProcessId);
if(hAppProcess == NULL)
{
return FALSE;
}
// Get handle to lsa process.
hLsaProcess = GetCurrentProcess();
// Duplicate handle
fResult = DuplicateHandle(hAppProcess,
hAppHandle,
hLsaProcess,
phLsaHandle,
0, FALSE,
DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
CloseHandle(hAppProcess);
CloseHandle(hLsaProcess);
return fResult;
}
//+---------------------------------------------------------------------------
//
// Function: RemoteCryptAcquireContextCallback
//
// Synopsis: Obtain a CSP context handle, using the information passed
// in the input buffer.
//
// Arguments: [dwProvType] -- Provider type.
// [dwFlags] -- Flags.
// [pInput] -- Buffer containing provider info.
// [pOutput] -- Buffer containing CSP context handle.
//
// History: 09-24-97 jbanes Created
//
// Notes: The structure of the input buffer is as follows:
//
// cbContainerName
// cbProvName
// dwCapiFlags
// wszContainerName
// wszProvName
//
// This function always uses an actual CSP.
//
//----------------------------------------------------------------------------
SECURITY_STATUS
RemoteCryptAcquireContextCallback(
ULONG_PTR dwProvType, // in
ULONG_PTR dwFlags, // in
SecBuffer *pInput, // in
SecBuffer *pOutput) // out
{
LPWSTR pwszContainerName;
DWORD cbContainerName;
LPWSTR pwszProvName;
DWORD cbProvName;
HCRYPTPROV hProv;
LPBYTE pbBuffer;
DWORD cbBuffer;
DWORD dwCapiFlags;
if(!SchannelInit(TRUE))
{
return SP_LOG_RESULT(SEC_E_INTERNAL_ERROR);
}
DebugLog((DEB_TRACE, "RemoteCryptAcquireContextCallback\n"));
pOutput->BufferType = SECBUFFER_DATA;
pOutput->cbBuffer = 0;
pOutput->pvBuffer = NULL;
if(pInput->pvBuffer == NULL)
{
return SP_LOG_RESULT(SEC_E_INTERNAL_ERROR);
}
// Parse input buffer.
pbBuffer = pInput->pvBuffer;
cbBuffer = pInput->cbBuffer;
if(cbBuffer < sizeof(DWORD) * 3)
{
return SP_LOG_RESULT(SEC_E_INCOMPLETE_MESSAGE);
}
cbContainerName = *(DWORD *)pbBuffer;
pbBuffer += sizeof(DWORD);
cbProvName = *(DWORD *)pbBuffer;
pbBuffer += sizeof(DWORD);
dwCapiFlags = *(DWORD *)pbBuffer;
pbBuffer += sizeof(DWORD);
if(cbBuffer < sizeof(DWORD) * 3 + cbContainerName + cbProvName)
{
return SP_LOG_RESULT(SEC_E_INCOMPLETE_MESSAGE);
}
if(cbContainerName)
{
pwszContainerName = (LPWSTR)pbBuffer;
}
else
{
pwszContainerName = NULL;
}
pbBuffer += cbContainerName;
if(cbProvName)
{
pwszProvName = (LPWSTR)pbBuffer;
}
else
{
pwszProvName = NULL;
}
// HACKHACK - clear the smart-card specific flag.
dwFlags &= ~CERT_SET_KEY_CONTEXT_PROP_ID;
DebugLog((SP_LOG_TRACE, "Container:%ls\n", pwszContainerName));
DebugLog((SP_LOG_TRACE, "Provider: %ls\n", pwszProvName));
DebugLog((SP_LOG_TRACE, "Type: 0x%8.8x\n", dwProvType));
DebugLog((SP_LOG_TRACE, "Flags: 0x%8.8x\n", dwFlags));
DebugLog((SP_LOG_TRACE, "CapiFlags:0x%8.8x\n", dwCapiFlags));
// Attempt to get CSP context handle.
if(!SchCryptAcquireContextW(&hProv,
pwszContainerName,
pwszProvName,
(DWORD) dwProvType,
(DWORD) dwFlags,
dwCapiFlags))
{
return SP_LOG_RESULT(GetLastError());
}
// Allocate memory for the output buffer.
pOutput->BufferType = SECBUFFER_DATA;
pOutput->cbBuffer = sizeof(HCRYPTPROV);
pOutput->pvBuffer = PvExtVirtualAlloc(pOutput->cbBuffer);
if(pOutput->pvBuffer == NULL)
{
return SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
}
// Place hProv in output buffer.
*(HCRYPTPROV *)pOutput->pvBuffer = hProv;
return SEC_E_OK;
}
NTSTATUS
RemoteCryptAcquireContextW(
HCRYPTPROV *phProv,
LPCWSTR pwszContainerName,
LPCWSTR pwszProvName,
DWORD dwProvType,
DWORD dwFlags,
DWORD dwCapiFlags)
{
SecBuffer Input;
SecBuffer Output;
DWORD cbContainerName;
DWORD cbProvName;
PBYTE pbBuffer;
SECURITY_STATUS scRet;
// Build input buffer.
if(pwszContainerName)
{
cbContainerName = (lstrlenW(pwszContainerName) + 1) * sizeof(WCHAR);
}
else
{
cbContainerName = 0;
}
if(pwszProvName)
{
cbProvName = (lstrlenW(pwszProvName) + 1) * sizeof(WCHAR);
}
else
{
cbProvName = 0;
}
Input.BufferType = SECBUFFER_DATA;
Input.cbBuffer = sizeof(DWORD) + cbContainerName +
sizeof(DWORD) + cbProvName +
sizeof(DWORD);
Input.pvBuffer = SPExternalAlloc(Input.cbBuffer);
if(Input.pvBuffer == NULL)
{
return SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
}
pbBuffer = Input.pvBuffer;
*(DWORD *)pbBuffer = cbContainerName;
pbBuffer += sizeof(DWORD);
*(DWORD *)pbBuffer = cbProvName;
pbBuffer += sizeof(DWORD);
*(DWORD *)pbBuffer = dwCapiFlags;
pbBuffer += sizeof(DWORD);
CopyMemory(pbBuffer, pwszContainerName, cbContainerName);
pbBuffer += cbContainerName;
CopyMemory(pbBuffer, pwszProvName, cbProvName);
pbBuffer += cbProvName;
// Do callback.
scRet = PerformApplicationCallback( SCH_ACQUIRE_CONTEXT_CALLBACK,
dwProvType,
dwFlags,
&Input,
&Output,
TRUE);
if(!NT_SUCCESS(scRet))
{
DebugLog((SP_LOG_ERROR, "Error 0x%x calling remote CryptAcquireContext\n", scRet));
SPExternalFree(Input.pvBuffer);
return scRet;
}
// Get hProv from output buffer.
*phProv = *(HCRYPTPROV *)Output.pvBuffer;
DebugLog((SP_LOG_TRACE, "Remote CSP handle retrieved (0x%x)\n", *phProv));
SPExternalFree(Input.pvBuffer);
SPExternalFree(Output.pvBuffer);
return SEC_E_OK;
}
SECURITY_STATUS
RemoteCryptReleaseContextCallback(
ULONG_PTR hProv, // in
ULONG_PTR dwFlags, // in
SecBuffer *pInput, // in
SecBuffer *pOutput) // out
{
DWORD dwCapiFlags;
if(!SchannelInit(TRUE))
{
return SP_LOG_RESULT(SEC_E_INTERNAL_ERROR);
}
DebugLog((DEB_TRACE, "RemoteCryptReleaseContextCallback\n"));
pOutput->BufferType = SECBUFFER_DATA;
pOutput->cbBuffer = 0;
pOutput->pvBuffer = NULL;
if(!CryptReleaseContext((HCRYPTPROV)hProv, (DWORD)dwFlags))
{
return SP_LOG_RESULT(GetLastError());
}
return SEC_E_OK;
}
BOOL
RemoteCryptReleaseContext(
HCRYPTPROV hProv,
DWORD dwFlags,
DWORD dwCapiFlags)
{
SecBuffer Input;
SecBuffer Output;
DWORD Status;
Input.BufferType = SECBUFFER_DATA;
Input.cbBuffer = 0;
Input.pvBuffer = NULL;
Status = PerformApplicationCallback(SCH_RELEASE_CONTEXT_CALLBACK,
(ULONG_PTR) hProv,
(ULONG_PTR) dwFlags,
&Input,
&Output,
FALSE);
if(!NT_SUCCESS(Status))
{
DebugLog((SP_LOG_ERROR, "Error 0x%x releasing crypto context!\n", Status));
SetLastError(Status);
return FALSE;
}
return TRUE;
}
//+---------------------------------------------------------------------------
//
// Function: UploadCertContextCallback
//
// Synopsis: Transfer a cert context structure from the application
// process to the LSA process.
//
// Arguments: [Argument1] -- Not used.
// [Argument2] -- Not used.
//
// [pInput] -- Buffer containing a cert context structure.
//
// [pOutput] -- Buffer containing the serialized certificate
// context, etc.
//
// History: 09-23-97 jbanes Created
//
// Notes: The structure of the output buffer is as follows:
//
// HCRYPTPROV hProv;
// DWORD cbSerializedCertContext;
// PVOID pvSerializedCertContext;
//
// This function always uses an actual CSP.
//
//----------------------------------------------------------------------------
SECURITY_STATUS
UploadCertContextCallback(
ULONG_PTR Argument1, // in
ULONG_PTR Argument2, // in
SecBuffer *pInput, // in
SecBuffer *pOutput) // out
{
PCCERT_CONTEXT pCertContext;
CRYPT_DATA_BLOB SaveBlob;
HCRYPTPROV hProv;
DWORD cbProvHandle;
DWORD cbCertContext;
PBYTE pbBuffer;
DWORD dwFlags;
SECURITY_STATUS scRet = SEC_E_UNKNOWN_CREDENTIALS;
if(!SchannelInit(TRUE))
{
return SP_LOG_RESULT(SEC_E_INTERNAL_ERROR);
}
DebugLog((DEB_TRACE, "UploadCertContextCallback\n"));
if(pInput->cbBuffer == 0 || pInput->pvBuffer == NULL)
{
pOutput->cbBuffer = 0;
pOutput->pvBuffer = NULL;
return SEC_E_OK;
}
else
{
pCertContext = *(PCCERT_CONTEXT *)pInput->pvBuffer;
}
pOutput->cbBuffer = 0;
pOutput->pvBuffer = NULL;
pOutput->BufferType = SECBUFFER_DATA;
// Attempt to read the hProv associated with the cert context.
cbProvHandle = sizeof(HCRYPTPROV);
if(!CertGetCertificateContextProperty(pCertContext,
CERT_KEY_PROV_HANDLE_PROP_ID,
(PVOID)&hProv,
&cbProvHandle))
{
hProv = 0;
cbProvHandle = sizeof(HCRYPTPROV);
}
// Determine the size of the serialized cert context.
if(!CertSerializeCertificateStoreElement(
pCertContext,
0,
NULL,
&cbCertContext))
{
scRet = SP_LOG_RESULT(SEC_E_UNKNOWN_CREDENTIALS);
goto Return;
}
//
// Build output buffer.
//
// Allocate memory for the output buffer.
pOutput->cbBuffer = sizeof(HCRYPTPROV) +
sizeof(DWORD) + cbCertContext;
pOutput->pvBuffer = PvExtVirtualAlloc(pOutput->cbBuffer);
if(pOutput->pvBuffer == NULL)
{
scRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
goto Return;
}
pbBuffer = pOutput->pvBuffer;
// Place hProv in output buffer.
*(HCRYPTPROV *)pbBuffer = hProv;
pbBuffer += sizeof(HCRYPTPROV);
// Place certificate context in output buffer.
*(DWORD *)pbBuffer = cbCertContext;
if(!CertSerializeCertificateStoreElement(
pCertContext,
0,
pbBuffer + sizeof(DWORD),
&cbCertContext))
{
scRet = SP_LOG_RESULT(SEC_E_UNKNOWN_CREDENTIALS);
goto Return;
}
scRet = SEC_E_OK;
Return:
if(!NT_SUCCESS(scRet) && (NULL != pOutput->pvBuffer))
{
SECURITY_STATUS Status;
Status = FreeExtVirtualAlloc(pOutput->pvBuffer, pOutput->cbBuffer);
SP_ASSERT(NT_SUCCESS(Status));
}
return scRet;
}
//+---------------------------------------------------------------------------
//
// Function: UploadCertStoreCallback
//
// Synopsis: Transfer a cert store from the application
// process to the LSA process, in the form of a serialized
// certificate store.
//
// Arguments: [Argument1] -- Not used.
// [Argument2] -- Not used.
//
// [pInput] -- Buffer containing a HCERTSTORE handle.
//
// [pOutput] -- Buffer containing the serialized cert store.
//
// History: 02-03-98 jbanes Created
//
// Notes: The structure of the output buffer is as follows:
//
// DWORD cbSerializedCertStore;
// PVOID pvSerializedCertStore;
//
// This function always uses an actual CSP.
//
//----------------------------------------------------------------------------
SECURITY_STATUS
UploadCertStoreCallback(
ULONG_PTR Argument1, // in
ULONG_PTR Argument2, // in
SecBuffer *pInput, // in
SecBuffer *pOutput) // out
{
HCERTSTORE hStore;
PCCERT_CONTEXT pCertContext;
PCCERT_CONTEXT pIssuer;
PCCERT_CONTEXT pPrevIssuer;
CRYPT_DATA_BLOB SaveBlob;
HCRYPTPROV hProv;
DWORD cbProvHandle;
DWORD cbCertContext;
DWORD cbCertStore;
PBYTE pbBuffer;
DWORD dwFlags;
if(!SchannelInit(TRUE))
{
return SP_LOG_RESULT(SEC_E_INTERNAL_ERROR);
}
DebugLog((DEB_TRACE, "UploadCertStoreCallback\n"));
pOutput->cbBuffer = 0;
pOutput->pvBuffer = NULL;
pOutput->BufferType = SECBUFFER_DATA;
if(pInput->cbBuffer != sizeof(HCERTSTORE) || pInput->pvBuffer == NULL)
{
return SP_LOG_RESULT(SEC_E_INTERNAL_ERROR);
}
hStore = *(HCERTSTORE *)pInput->pvBuffer;
// Determine the size of the serialized store.
SaveBlob.cbData = 0;
SaveBlob.pbData = NULL;
if(!CertSaveStore(hStore,
X509_ASN_ENCODING,
CERT_STORE_SAVE_AS_STORE,
CERT_STORE_SAVE_TO_MEMORY,
(PVOID)&SaveBlob,
0))
{
return SP_LOG_RESULT(SEC_E_UNKNOWN_CREDENTIALS);
}
cbCertStore = SaveBlob.cbData;
//
// Build output buffer.
//
// Allocate memory for the output buffer.
pOutput->cbBuffer = sizeof(DWORD) + cbCertStore;
pOutput->pvBuffer = PvExtVirtualAlloc(pOutput->cbBuffer);
if(pOutput->pvBuffer == NULL)
{
return SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
}
pbBuffer = pOutput->pvBuffer;
// Place certificate store in output buffer.
*(DWORD *)pbBuffer = cbCertStore;
SaveBlob.cbData = cbCertStore;
SaveBlob.pbData = pbBuffer + sizeof(DWORD);
if(!CertSaveStore(hStore,
X509_ASN_ENCODING,
CERT_STORE_SAVE_AS_STORE,
CERT_STORE_SAVE_TO_MEMORY,
(PVOID)&SaveBlob,
0))
{
FreeExtVirtualAlloc(pOutput->pvBuffer, pOutput->cbBuffer);
return SP_LOG_RESULT(SEC_E_UNKNOWN_CREDENTIALS);
}
return SEC_E_OK;
}
SP_STATUS
SignHashUsingCallback(
HCRYPTPROV hProv,
DWORD dwKeySpec,
ALG_ID aiHash,
PBYTE pbHash,
DWORD cbHash,
PBYTE pbSignature,
PDWORD pcbSignature,
DWORD fHashData)
{
SecBuffer Input;
SecBuffer Output;
SP_STATUS pctRet;
//
// Build input buffer.
//
Input.BufferType = SECBUFFER_DATA;
Input.cbBuffer = sizeof(DWORD) * 2 + cbHash;
Input.pvBuffer = SPExternalAlloc(Input.cbBuffer);
if(Input.pvBuffer == NULL)
{
return SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
}
memcpy(Input.pvBuffer, (PBYTE)&dwKeySpec, sizeof(DWORD));
memcpy((PBYTE)Input.pvBuffer + sizeof(DWORD), (PBYTE)&fHashData, sizeof(DWORD));
memcpy((PBYTE)Input.pvBuffer + sizeof(DWORD) * 2,
pbHash,
cbHash);
//
// Callback into application process.
//
pctRet = PerformApplicationCallback(SCH_SIGNATURE_CALLBACK,
hProv,
aiHash,
&Input,
&Output,
TRUE);
SPExternalFree(Input.pvBuffer);
if(pctRet != PCT_ERR_OK)
{
return pctRet;
}
if(Output.cbBuffer > *pcbSignature)
{
*pcbSignature = Output.cbBuffer;
SPExternalFree(Output.pvBuffer);
return SP_LOG_RESULT(SEC_E_BUFFER_TOO_SMALL);
}
*pcbSignature = Output.cbBuffer;
memcpy(pbSignature, Output.pvBuffer, Output.cbBuffer);
SPExternalFree(Output.pvBuffer);
return PCT_ERR_OK;
}
//+---------------------------------------------------------------------------
//
// Function: SPSignatureCallback
//
// Synopsis: Perform signature, using application's hProv
//
// Arguments: [hProv] --
// [aiHash] --
// [pInput] --
// [pOutput] --
//
// History: 09-23-97 jbanes Created
//
// Notes: This function always uses an actual CSP.
//
//----------------------------------------------------------------------------
SECURITY_STATUS
SPSignatureCallback(ULONG_PTR hProv, // in
ULONG_PTR aiHash, // in
SecBuffer *pInput, // in
SecBuffer *pOutput) // out
{
HCRYPTHASH hHash;
DWORD dwKeySpec;
DWORD fHashData;
PBYTE pbHash;
DWORD cbHash;
SP_STATUS pctRet;
if(!SchannelInit(TRUE))
{
return SP_LOG_RESULT(SEC_E_INTERNAL_ERROR);
}
DebugLog((DEB_TRACE, "SPSignatureCallback\n"));
//
// Parse input buffer.
//
if(pInput->cbBuffer < sizeof(DWORD) * 2)
{
return SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE);
}
memcpy(&dwKeySpec, pInput->pvBuffer, sizeof(DWORD));
memcpy(&fHashData, (PBYTE)pInput->pvBuffer + sizeof(DWORD), sizeof(DWORD));
pbHash = (PBYTE)pInput->pvBuffer + sizeof(DWORD) * 2;
cbHash = pInput->cbBuffer - sizeof(DWORD) * 2;
//
// Prepare hash object.
//
if(!CryptCreateHash(hProv, (ALG_ID)aiHash, 0, 0, &hHash))
{
SP_LOG_RESULT( GetLastError() );
return PCT_ERR_ILLEGAL_MESSAGE;
}
if(!fHashData)
{
// set hash value
if(!CryptSetHashParam(hHash, HP_HASHVAL, pbHash, 0))
{
SP_LOG_RESULT( GetLastError() );
CryptDestroyHash(hHash);
return PCT_ERR_ILLEGAL_MESSAGE;
}
}
else
{
if(!CryptHashData(hHash, pbHash, cbHash, 0))
{
SP_LOG_RESULT( GetLastError() );
CryptDestroyHash(hHash);
return PCT_ERR_ILLEGAL_MESSAGE;
}
}
//
// Sign hash.
//
pOutput->BufferType = SECBUFFER_DATA;
// Get size of signature
if(!CryptSignHash(hHash, dwKeySpec, NULL, 0, NULL, &pOutput->cbBuffer))
{
pctRet = SP_LOG_RESULT(GetLastError());
CryptDestroyHash(hHash);
return pctRet;
}
// Allocate memory
pOutput->pvBuffer = PvExtVirtualAlloc(pOutput->cbBuffer);
if(pOutput->pvBuffer == NULL)
{
CryptDestroyHash(hHash);
return SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
}
// Sign hash.
if(!CryptSignHash(hHash, dwKeySpec, NULL, 0, pOutput->pvBuffer, &pOutput->cbBuffer))
{
pctRet = SP_LOG_RESULT(GetLastError());
CryptDestroyHash(hHash);
FreeExtVirtualAlloc(pOutput->pvBuffer, pOutput->cbBuffer);
return pctRet;
}
CryptDestroyHash(hHash);
return PCT_ERR_OK;
}
//+---------------------------------------------------------------------------
//
// Function: DownloadCertContextCallBack
//
// Synopsis: Transfer a cert context structure from the application
// process to the LSA process, in the form of a serialized
// certificate store.
//
// Arguments: [Argument1] -- Not used.
// [Argument2] -- Not used.
// [pInput] --
// [pOutput] --
//
// History: 09-26-97 jbanes Created
//
// Notes: The structure of the input buffer is as follows:
//
// DWORD cbSerializedCertStore;
// PVOID pvSerializedCertStore;
// DWORD cbSerializedCertContext;
// PVOID pvSerializedCertContext;
//
// This function always uses an actual CSP.
//
//----------------------------------------------------------------------------
SECURITY_STATUS
DownloadCertContextCallback(
ULONG_PTR Argument1, // in
ULONG_PTR Argument2, // in
SecBuffer *pInput, // in
SecBuffer *pOutput) // out
{
PCCERT_CONTEXT pCertContext;
SECURITY_STATUS scRet;
if(!SchannelInit(TRUE))
{
return SP_LOG_RESULT(SEC_E_INTERNAL_ERROR);
}
DebugLog((DEB_TRACE, "DownloadCertContextCallback\n"));
// Allocate memory for output buffer.
pOutput->BufferType = SECBUFFER_DATA;
pOutput->cbBuffer = sizeof(PVOID);
pOutput->pvBuffer = PvExtVirtualAlloc(pOutput->cbBuffer);
if(pOutput->pvBuffer == NULL)
{
return SP_LOG_RESULT( SEC_E_INSUFFICIENT_MEMORY );
}
// Deserialize buffer.
scRet = DeserializeCertContext(&pCertContext,
pInput->pvBuffer,
pInput->cbBuffer);
if(FAILED(scRet))
{
FreeExtVirtualAlloc(pOutput->pvBuffer, pOutput->cbBuffer);
return SP_LOG_RESULT( scRet );
}
// Place cert context pointer in output buffer.
*(PCCERT_CONTEXT *)pOutput->pvBuffer = pCertContext;
return SEC_E_OK;
}
//+---------------------------------------------------------------------------
//
// Function: SerializeCertContext
//
// Synopsis: Serialize the specified certificate context, along with its
// associated certificate store.
//
// Arguments: [pCertContext] --
// [pbBuffer] --
// [pcbBuffer] --
//
// History: 09-26-97 jbanes Created
//
// Notes: The structure of the output buffer is as follows:
//
// DWORD cbSerializedCertStore
// PVOID pvSerializedCertStore
// DWORD cbSerializedCertContext
// PVOID pvSerializedCertContext
//
//----------------------------------------------------------------------------
SECURITY_STATUS
SerializeCertContext(
PCCERT_CONTEXT pCertContext, // in
PBYTE pbBuffer, // out
PDWORD pcbBuffer) // out
{
CRYPT_DATA_BLOB SaveBlob;
DWORD cbCertContext;
DWORD cbCertStore;
DWORD cbBuffer;
if(pCertContext == NULL)
{
*pcbBuffer = 0;
return SEC_E_OK;
}
// Determine the size of the serialized cert context.
if(!CertSerializeCertificateStoreElement(
pCertContext,
0,
NULL,
&cbCertContext))
{
return SP_LOG_RESULT(SEC_E_UNKNOWN_CREDENTIALS);
}
// Determine the size of the serialized store.
if(pCertContext->hCertStore)
{
SaveBlob.cbData = 0;
SaveBlob.pbData = NULL;
if(!CertSaveStore(pCertContext->hCertStore,
X509_ASN_ENCODING,
CERT_STORE_SAVE_AS_STORE,
CERT_STORE_SAVE_TO_MEMORY,
(PVOID)&SaveBlob,
0))
{
return SP_LOG_RESULT(SEC_E_UNKNOWN_CREDENTIALS);
}
cbCertStore = SaveBlob.cbData;
}
else
{
cbCertStore = 0;
}
cbBuffer = sizeof(DWORD) + cbCertContext +
sizeof(DWORD) + cbCertStore;
if(pbBuffer == NULL)
{
*pcbBuffer = cbBuffer;
return SEC_E_OK;
}
if(*pcbBuffer < cbBuffer)
{
return SP_LOG_RESULT(SEC_E_BUFFER_TOO_SMALL);
}
// Set output values.
*pcbBuffer = cbBuffer;
// Place certificate store in output buffer.
*(DWORD *)pbBuffer = cbCertStore;
if(pCertContext->hCertStore)
{
SaveBlob.cbData = cbCertStore;
SaveBlob.pbData = pbBuffer + sizeof(DWORD);
if(!CertSaveStore(pCertContext->hCertStore,
X509_ASN_ENCODING,
CERT_STORE_SAVE_AS_STORE,
CERT_STORE_SAVE_TO_MEMORY,
(PVOID)&SaveBlob,
0))
{
return SP_LOG_RESULT(SEC_E_UNKNOWN_CREDENTIALS);
}
}
pbBuffer += sizeof(DWORD) + cbCertStore;
// Place certificate context in output buffer.
*(DWORD UNALIGNED *)pbBuffer = cbCertContext;
if(!CertSerializeCertificateStoreElement(
pCertContext,
0,
pbBuffer + sizeof(DWORD),
&cbCertContext))
{
return SP_LOG_RESULT(SEC_E_UNKNOWN_CREDENTIALS);
}
return SEC_E_OK;
}
SECURITY_STATUS
DeserializeCertContext(
PCCERT_CONTEXT *ppCertContext, // out
PBYTE pbBuffer, // in
DWORD cbBuffer) // in
{
CRYPT_DATA_BLOB Serialized;
HCERTSTORE hStore;
// Deserialize certificate store.
Serialized.cbData = *(DWORD *)pbBuffer;
Serialized.pbData = pbBuffer + sizeof(DWORD);
hStore = CertOpenStore( CERT_STORE_PROV_SERIALIZED,
X509_ASN_ENCODING,
0,
CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG,
&Serialized);
if(hStore == NULL)
{
return SP_LOG_RESULT(SEC_E_UNKNOWN_CREDENTIALS);
}
pbBuffer += sizeof(DWORD) + Serialized.cbData;
// Deserialize certificate context.
if(!CertAddSerializedElementToStore(hStore,
pbBuffer + sizeof(DWORD),
*(DWORD UNALIGNED *)pbBuffer,
CERT_STORE_ADD_USE_EXISTING,
0,
CERT_STORE_CERTIFICATE_CONTEXT_FLAG,
NULL,
ppCertContext))
{
CertCloseStore(hStore, 0);
return SP_LOG_RESULT(SEC_E_UNKNOWN_CREDENTIALS);
}
if(!CertCloseStore(hStore, 0))
{
SP_LOG_RESULT(GetLastError());
}
return SEC_E_OK;
}
//+---------------------------------------------------------------------------
//
// Function: SPGetApplicationKeys
//
// Synopsis: Callback to the user process and retrieve the user encryption
// keys.
//
// Arguments: [pContext] -- Schannel context.
// [dwFlags] -- SCH_FLAG_READ_KEY, SCH_FLAG_WRITE_KEY
//
// History: 10-17-97 jbanes Created
//
// Notes:
//
//----------------------------------------------------------------------------
SP_STATUS
SPGetUserKeys(
PSPContext pContext,
DWORD dwFlags)
{
PBYTE pbBuffer;
PBYTE pbReadKey;
DWORD cbReadKey;
PBYTE pbWriteKey;
DWORD cbWriteKey;
SecBuffer Input;
SecBuffer Output;
SECURITY_STATUS scRet;
SECPKG_CALL_INFO CallInfo;
BOOL fWow64Client = FALSE;
//
// Call back into the application process and get the keys, in the
// form of 2 opaque blobs.
//
DebugLog((SP_LOG_TRACE, "SPGetUserKeys: 0x%p, %d\n", pContext, dwFlags));
#ifdef _WIN64
if(!LsaTable->GetCallInfo(&CallInfo))
{
scRet = STATUS_INTERNAL_ERROR;
return SP_LOG_RESULT(scRet);
}
fWow64Client = (CallInfo.Attributes & SECPKG_CALL_WOWCLIENT) != 0;
#endif
if(fWow64Client)
{
Input.BufferType = SECBUFFER_DATA;
Input.cbBuffer = sizeof(pContext->ContextThumbprint);
Input.pvBuffer = &pContext->ContextThumbprint;
}
else
{
Input.BufferType = SECBUFFER_DATA;
Input.cbBuffer = 0;
Input.pvBuffer = NULL;
}
scRet = PerformApplicationCallback( SCH_GET_USER_KEYS,
(ULONG_PTR) pContext,
(ULONG_PTR) dwFlags,
&Input,
&Output,
TRUE);
if(!NT_SUCCESS(scRet))
{
DebugLog((SP_LOG_ERROR, "Error 0x%x retrieving user keys\n", scRet));
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
}
//
// Parse output buffer
//
pbBuffer = Output.pvBuffer;
if(dwFlags & SCH_FLAG_READ_KEY)
{
pContext->ReadCounter = *(PDWORD)pbBuffer;
}
pbBuffer += sizeof(DWORD);
if(dwFlags & SCH_FLAG_WRITE_KEY)
{
pContext->WriteCounter = *(PDWORD)pbBuffer;
}
pbBuffer += sizeof(DWORD);
cbReadKey = *(PDWORD)pbBuffer;
pbBuffer += sizeof(DWORD);
cbWriteKey = *(PDWORD)pbBuffer;
pbBuffer += sizeof(DWORD);
pbReadKey = pbBuffer;
pbBuffer += cbReadKey;
pbWriteKey = pbBuffer;
pbBuffer += cbWriteKey;
SP_ASSERT(pbBuffer - (PBYTE)Output.pvBuffer == (INT)Output.cbBuffer);
//
// Place keys into context structure.
//
if(dwFlags & SCH_FLAG_READ_KEY)
{
if(cbReadKey)
{
if(!SchCryptImportKey(pContext->RipeZombie->hMasterProv,
pbReadKey,
cbReadKey,
0,
CRYPT_EXPORTABLE,
&pContext->hReadKey,
0))
{
SP_LOG_RESULT(GetLastError());
scRet = PCT_INT_INTERNAL_ERROR;
goto done;
}
}
else
{
pContext->hReadKey = 0;
}
}
if(dwFlags & SCH_FLAG_WRITE_KEY)
{
if(cbWriteKey)
{
if(!SchCryptImportKey(pContext->RipeZombie->hMasterProv,
pbWriteKey,
cbWriteKey,
0,
CRYPT_EXPORTABLE,
&pContext->hWriteKey,
0))
{
SP_LOG_RESULT(GetLastError());
scRet = PCT_INT_INTERNAL_ERROR;
goto done;
}
}
else
{
pContext->hWriteKey = 0;
}
}
done:
SPExternalFree(Output.pvBuffer);
return scRet;
}
//+---------------------------------------------------------------------------
//
// Function: GetUserKeysCallback
//
// Synopsis: Find the user context that corresponds to the passed in LSA
// context, serialize the encryption keys, and return them in
// the output buffer.
//
// Arguments: [dwLsaContext] -- Pointer to LSA Schannel context.
// [dwFlags] -- SCH_FLAG_READ_KEY, SCH_FLAG_WRITE_KEY
// [pInput] -- Not used.
// [pOutput] -- (output) Serialized keys.
//
// History: 10-17-97 jbanes Created
//
// Notes: The structure of the output buffer is as follows:
//
// DWORD dwReadSequence;
// DWORD dwWriteSequence;
// DWORD cbReadKey;
// BYTE rgbReadKey[];
// DWORD cbWriteKey;
// BYTE rgbWriteKey[];
//
//----------------------------------------------------------------------------
SECURITY_STATUS
GetUserKeysCallback(
ULONG_PTR dwLsaContext,
ULONG_PTR dwFlags,
SecBuffer *pInput,
SecBuffer *pOutput)
{
DWORD cbReadKey = 0;
DWORD cbWriteKey = 0;
DWORD cbData;
PBYTE pbBuffer;
DWORD cbBuffer;
PSPContext pContext;
SECURITY_STATUS scRet;
PSSL_USER_CONTEXT pUserContext;
if(!SchannelInit(TRUE))
{
return SP_LOG_RESULT(SEC_E_INTERNAL_ERROR);
}
DebugLog((DEB_TRACE, "GetUserKeysCallback\n"));
//
// Find the user context.
//
if(pInput->pvBuffer != NULL &&
pInput->cbBuffer == sizeof(CRED_THUMBPRINT))
{
// Search for matching context thumbprint.
pUserContext = SslFindUserContextEx((PCRED_THUMBPRINT)pInput->pvBuffer);
if(pUserContext == NULL)
{
return SP_LOG_RESULT( SEC_E_INVALID_HANDLE );
}
}
else
{
// Search for matching lsa context
pUserContext = SslFindUserContext(dwLsaContext);
if(pUserContext == NULL)
{
return SP_LOG_RESULT( SEC_E_INVALID_HANDLE );
}
}
pContext = pUserContext->pContext;
if(pContext == NULL)
{
return SP_LOG_RESULT( SEC_E_INTERNAL_ERROR );
}
//
// Compute size of output buffer.
//
if(dwFlags & SCH_FLAG_READ_KEY)
{
if(pContext->pReadCipherInfo->aiCipher != CALG_NULLCIPHER)
{
if(!pContext->hReadKey)
{
return SP_LOG_RESULT( SEC_E_INVALID_HANDLE );
}
if(!SchCryptExportKey(pContext->hReadKey,
0, OPAQUEKEYBLOB, 0,
NULL,
&cbReadKey,
0))
{
SP_LOG_RESULT(GetLastError());
return SEC_E_INTERNAL_ERROR;
}
}
else
{
cbReadKey = 0;
}
}
if(dwFlags & SCH_FLAG_WRITE_KEY)
{
if(pContext->pWriteCipherInfo->aiCipher != CALG_NULLCIPHER)
{
if(!pContext->hWriteKey)
{
return SP_LOG_RESULT( SEC_E_INVALID_HANDLE );
}
if(!SchCryptExportKey(pContext->hWriteKey,
0, OPAQUEKEYBLOB, 0,
NULL,
&cbWriteKey,
0))
{
SP_LOG_RESULT(GetLastError());
return SEC_E_INTERNAL_ERROR;
}
}
else
{
cbWriteKey = 0;
}
}
cbBuffer = sizeof(DWORD) +
sizeof(DWORD) +
sizeof(DWORD) + cbReadKey +
sizeof(DWORD) + cbWriteKey;
// Allocate memory for output buffer.
pbBuffer = PvExtVirtualAlloc( cbBuffer);
if(pbBuffer == NULL)
{
return SP_LOG_RESULT( SEC_E_INSUFFICIENT_MEMORY );
}
pOutput->BufferType = SECBUFFER_DATA;
pOutput->cbBuffer = cbBuffer;
pOutput->pvBuffer = pbBuffer;
//
// Serialize keys.
//
*(PDWORD)pbBuffer = pContext->ReadCounter;
pbBuffer += sizeof(DWORD);
*(PDWORD)pbBuffer = pContext->WriteCounter;
pbBuffer += sizeof(DWORD);
*(PDWORD)pbBuffer = cbReadKey;
pbBuffer += sizeof(DWORD);
*(PDWORD)pbBuffer = cbWriteKey;
pbBuffer += sizeof(DWORD);
if(dwFlags & SCH_FLAG_READ_KEY)
{
if(pContext->pReadCipherInfo->aiCipher != CALG_NULLCIPHER)
{
cbData = cbReadKey;
if(!SchCryptExportKey(pContext->hReadKey,
0, OPAQUEKEYBLOB, 0,
pbBuffer,
&cbData,
0))
{
FreeExtVirtualAlloc(pOutput->pvBuffer, pOutput->cbBuffer);
SP_LOG_RESULT(GetLastError());
return SEC_E_INTERNAL_ERROR;
}
if(!SchCryptDestroyKey(pContext->hReadKey, 0))
{
SP_LOG_RESULT(GetLastError());
}
}
pContext->hReadKey = 0;
}
pbBuffer += cbReadKey;
if(dwFlags & SCH_FLAG_WRITE_KEY)
{
if(pContext->pWriteCipherInfo->aiCipher != CALG_NULLCIPHER)
{
cbData = cbWriteKey;
if(!SchCryptExportKey(pContext->hWriteKey,
0, OPAQUEKEYBLOB, 0,
pbBuffer,
&cbData,
0))
{
FreeExtVirtualAlloc(pOutput->pvBuffer, pOutput->cbBuffer);
SP_LOG_RESULT(GetLastError());
return SEC_E_INTERNAL_ERROR;
}
if(!SchCryptDestroyKey(pContext->hWriteKey, 0))
{
SP_LOG_RESULT(GetLastError());
}
}
pContext->hWriteKey = 0;
}
pbBuffer += cbWriteKey;
return SEC_E_OK;
}
// Always called from callback routine (in the application process).
VOID *
PvExtVirtualAlloc(DWORD cb)
{
SECURITY_STATUS Status;
PVOID pv = NULL;
SIZE_T Size = cb;
Status = NtAllocateVirtualMemory(
GetCurrentProcess(),
&pv,
0,
&Size,
MEM_COMMIT,
PAGE_READWRITE);
if(!NT_SUCCESS(Status))
{
pv = NULL;
}
DebugLog((DEB_TRACE, "SslCallbackVirtualAlloc: 0x%x bytes at 0x%x\n", cb, pv));
return(pv);
}
// Always called from callback routine (in the application process),
// typically when an error occurs and we're cleaning up.
SECURITY_STATUS
FreeExtVirtualAlloc(PVOID pv, SIZE_T cbMem)
{
cbMem = 0;
return(NtFreeVirtualMemory(GetCurrentProcess(),
&pv,
&cbMem,
MEM_RELEASE));
}
// Always called from the LSA process, when freeing memory allocated
// by a callback function.
SECURITY_STATUS
SPFreeUserAllocMemory(PVOID pv, SIZE_T cbMem)
{
SECPKG_CALL_INFO CallInfo;
if(LsaTable->GetCallInfo(&CallInfo))
{
SECURITY_STATUS Status;
HANDLE hProcess;
hProcess = OpenProcess(PROCESS_VM_OPERATION,
FALSE,
CallInfo.ProcessId);
if(hProcess == NULL)
{
return SP_LOG_RESULT(GetLastError());
}
cbMem = 0;
Status = NtFreeVirtualMemory(hProcess,
&pv,
&cbMem,
MEM_RELEASE);
if(!NT_SUCCESS(Status))
{
SP_LOG_RESULT(Status);
}
CloseHandle(hProcess);
}
DebugLog((DEB_TRACE, "SslCallbackVirtualFree: 0x%x bytes at 0x%x\n", cbMem, pv));
return SEC_E_OK;
}