Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1482 lines
43 KiB

/*
File: crypt32.cpp
Title: CryptProtect APIs
Author: Matt Thomlinson
Date: 8/2/97
The CryptProtect API set allows an application to secure
user data for online and offline storage. While the well-known problem
of data storage is left to the calling application, this solves the
relatively unsolved problem of how to cryptographically derive strong
keys for storing the data. These APIs are initially available on NT5 only.
Very little checking is done at this level to validate the caller. We
believe that problem should be solved at a different level -- since all
other system security is granular to the user, it is difficult to create a
feature that provides something more granular. Instead, any process running
under the logged-in user has the ability to decrypt any and all items it
can retrieve. Callers should note that while items are being processed,
UI may be spawned to notify the user.
For user confirmation, the NT secure attention sequence is used to
garner the wishes of the user. This behavior is set by the caller during protection.
*/
#include <pch.cpp>
#pragma hdrstop
#include "msaudite.h"
#include "crypt.h"
#define ALGID_DERIVEKEY_HASH CALG_SHA1 // doesn't change
// USEC: can be as long as we want, since we castrate generated key later
#define KEY_DERIVATION_BUFSIZE (128/8)
#define DEFAULT_BLOCKSIZE_OVERRUN (128/8) // allow block ciphers to process up to a 128 bits at a time
#define MS_BASE_CRYPTPROTECT_VERSION 0x01
DWORD
WINAPI
SPCryptProtect(
PVOID pvContext, // server context
PBYTE* ppbOut, // out encr data
DWORD* pcbOut, // out encr cb
PBYTE pbIn, // in ptxt data
DWORD cbIn, // in ptxt cb
LPCWSTR szDataDescr, // in
PBYTE pbOptionalEntropy, // OPTIONAL
DWORD cbOptionalEntropy,
PSSCRYPTPROTECTDATA_PROMPTSTRUCT psPrompt, // OPTIONAL prompting struct
DWORD dwFlags,
BYTE* pbOptionalPassword,
DWORD cbOptionalPassword
)
{
DWORD dwRet;
LPWSTR szUser = NULL;
PBYTE pbWritePtr = NULL;
GUID guidMK;
WCHAR wszMKGuidString[MAX_GUID_SZ_CHARS];
HCRYPTPROV hVerifyProv;
HCRYPTHASH hHash = NULL;
HCRYPTKEY hKey = NULL;
PBYTE pbCrypt = NULL;
DWORD cbCrypt = 0;
DWORD cbEncrypted; // count of bytes to encrypt
PBYTE pbEncrSalt = NULL;
DWORD cbEncrSalt = 0;
BYTE rgbPwdBuf[A_SHA_DIGEST_LEN];
DWORD cbMACSize = A_SHA_DIGEST_LEN;
BYTE rgbEncrKey[KEY_DERIVATION_BUFSIZE];
BYTE rgbMACKey[KEY_DERIVATION_BUFSIZE];
LPBYTE pbMasterKey = NULL;
DWORD cbMasterKey = 0;
PBYTE pStreamFlagPtr;
DWORD dwProtectionFlags = 0;
DWORD dwDefaultCryptProvType = 0;
DWORD dwAlgID_Encr_Alg = 0;
DWORD dwAlgID_Encr_Alg_KeySize = 0;
DWORD dwAlgID_MAC_Alg = 0;
DWORD dwAlgID_MAC_Alg_KeySize = 0;
PCRYPT_SERVER_CONTEXT pServerContext = (PCRYPT_SERVER_CONTEXT)pvContext;
#if DBG
D_DebugLog((DEB_TRACE_API,"SPCryptProtect 0x%x called\n", pServerContext));
if(pServerContext)
{
if(pServerContext->cbSize == sizeof(CRYPT_SERVER_CONTEXT))
{
D_DebugLog((DEB_TRACE_API, " pServerContext->hBinding:%d\n", pServerContext->hBinding));
D_DebugLog((DEB_TRACE_API, " pServerContext->fOverrideToLocalSystem:%d\n", pServerContext->fOverrideToLocalSystem));
D_DebugLog((DEB_TRACE_API, " pServerContext->fImpersonating:%d\n", pServerContext->fImpersonating));
D_DebugLog((DEB_TRACE_API, " pServerContext->hToken:%d\n", pServerContext->hToken));
D_DebugLog((DEB_TRACE_API, " pServerContext->szUserStorageArea:%ls\n", pServerContext->szUserStorageArea));
D_DebugLog((DEB_TRACE_API, " pServerContext->WellKnownAccount:%d\n", pServerContext->WellKnownAccount));
}
}
D_DebugLog((DEB_TRACE_API, " pbInput:0x%x\n", pbIn));
D_DebugLog((DEB_TRACE_API, " pszDataDescr:%ls\n", szDataDescr));
D_DebugLog((DEB_TRACE_API, " pbOptionalEntropy:0x%x\n", pbOptionalEntropy));
D_DPAPIDumpHexData(DEB_TRACE_BUFFERS, " ", pbOptionalEntropy, cbOptionalEntropy);
D_DebugLog((DEB_TRACE_API, " dwFlags:0x%x\n", dwFlags));
D_DebugLog((DEB_TRACE_API, " pbOptionalPassword:0x%x\n", pbOptionalPassword));
D_DPAPIDumpHexData(DEB_TRACE_BUFFERS, " ", pbOptionalPassword, cbOptionalPassword);
#endif
ZeroMemory(&guidMK, sizeof(guidMK));
wszMKGuidString[0] = 0;
GetDefaultAlgInfo(&dwDefaultCryptProvType,
&dwAlgID_Encr_Alg,
&dwAlgID_Encr_Alg_KeySize,
&dwAlgID_MAC_Alg,
&dwAlgID_MAC_Alg_KeySize);
if( dwFlags & CRYPTPROTECT_LOCAL_MACHINE ) {
BOOL fOverrideToLocalSystem = TRUE;
CPSOverrideToLocalSystem(
pvContext,
&fOverrideToLocalSystem,
NULL // don't care what previous value was
);
dwProtectionFlags |= CRYPTPROTECT_LOCAL_MACHINE;
}
if( dwFlags & CRYPTPROTECT_CRED_SYNC )
{
if(dwFlags != CRYPTPROTECT_CRED_SYNC)
{
// If the user is reencrypting master keys, then no other flags
// may be specified. Lets do one thing at a time, shall we?
D_DebugLog((DEB_ERROR, "SPCryptProtect: Invalid flags 0x%x\n", dwFlags));
return ERROR_INVALID_PARAMETER;
}
dwRet = CPSImpersonateClient( pvContext );
if( dwRet == ERROR_SUCCESS )
{
dwRet = InitiateSynchronizeMasterKeys( pvContext );
CPSRevertToSelf( pvContext );
}
return dwRet;
}
if( dwFlags & CRYPTPROTECT_CRED_REGENERATE )
{
if(dwFlags != CRYPTPROTECT_CRED_REGENERATE)
{
// If the user is reencrypting master keys, then no other flags
// may be specified. Lets do one thing at a time, shall we?
D_DebugLog((DEB_ERROR, "SPCryptProtect: Invalid flags 0x%x\n", dwFlags));
return ERROR_INVALID_PARAMETER;
}
dwRet = DpapiUpdateLsaSecret( pvContext );
return dwRet;
}
//
// include additional flags
//
dwProtectionFlags |= (dwFlags & (CRYPTPROTECT_SYSTEM | CRYPTPROTECT_AUDIT));
// else no override; get provider by algs alone
if (NULL == (hVerifyProv =
GetCryptProviderHandle(
dwDefaultCryptProvType,
dwAlgID_Encr_Alg, &dwAlgID_Encr_Alg_KeySize,
dwAlgID_MAC_Alg, &dwAlgID_MAC_Alg_KeySize)) )
{
dwRet = GetLastError();
goto Ret;
}
dwRet = GetSpecifiedMasterKey(
pvContext,
&guidMK,
&pbMasterKey,
&cbMasterKey,
FALSE // we don't know what master key we want to use - use preferred
);
if(dwRet != ERROR_SUCCESS)
goto Ret;
MyGuidToStringW(&guidMK, wszMKGuidString);
//
// hash pbMasterKey to get rgbPwdBuf
//
FMyPrimitiveSHA( pbMasterKey, cbMasterKey, rgbPwdBuf );
// derive encr key
{
if (!RtlGenRandom(
rgbEncrKey,
sizeof(rgbEncrKey)))
{
dwRet = GetLastError();
goto Ret;
}
#if DBG
// Leave here as regression check
CheckMACInterop(rgbPwdBuf, sizeof(rgbPwdBuf), rgbEncrKey, sizeof(rgbEncrKey), hVerifyProv, ALGID_DERIVEKEY_HASH);
#endif
if (!FMyPrimitiveCryptHMAC(
rgbPwdBuf,
sizeof(rgbPwdBuf),
rgbEncrKey,
sizeof(rgbEncrKey),
hVerifyProv,
ALGID_DERIVEKEY_HASH,
&hHash))
{
dwRet = GetLastError();
goto Ret;
}
// add password if exists
if (NULL != pbOptionalEntropy)
{
if (!CryptHashData(
hHash,
pbOptionalEntropy,
cbOptionalEntropy,
0))
{
dwRet = GetLastError();
goto Ret;
}
}
// add prompted UI based password if exists.
// will eventually come from SAS
//
if ( NULL != pbOptionalPassword && cbOptionalPassword )
{
if (!CryptHashData(
hHash,
pbOptionalPassword,
cbOptionalPassword,
0))
{
dwRet = GetLastError();
goto Ret;
}
}
if (!CryptDeriveKey(
hVerifyProv,
dwAlgID_Encr_Alg,
hHash,
CRYPT_CREATE_SALT,
&hKey))
{
dwRet = GetLastError();
goto Ret;
}
CryptDestroyHash(hHash);
hHash = 0;
// USEC -- (US Export Controls)
if (ERROR_SUCCESS != (dwRet =
GetSaltForExportControl(
hVerifyProv,
hKey,
&pbEncrSalt,
&cbEncrSalt)) )
goto Ret;
}
// derive MAC key
{
if (!RtlGenRandom(
rgbMACKey,
sizeof(rgbMACKey)))
{
dwRet = GetLastError();
goto Ret;
}
if (!FMyPrimitiveCryptHMAC(
rgbPwdBuf,
sizeof(rgbPwdBuf),
rgbMACKey,
sizeof(rgbMACKey),
hVerifyProv,
dwAlgID_MAC_Alg,
&hHash))
{
dwRet = GetLastError();
goto Ret;
}
// add password if exists
if (NULL != pbOptionalEntropy)
{
if (!CryptHashData(
hHash,
pbOptionalEntropy,
cbOptionalEntropy,
0))
{
dwRet = GetLastError();
goto Ret;
}
}
// add prompted UI based password if exists.
// will eventually come from SAS
//
if ( NULL != pbOptionalPassword && cbOptionalPassword )
{
if (!CryptHashData(
hHash,
pbOptionalPassword,
cbOptionalPassword,
0))
{
dwRet = GetLastError();
goto Ret;
}
}
// USEC -- (US Export Controls)
// Does not apply to MAC -- use strong key
}
// Decrypt the input buffer.
if((dwFlags & CRYPTPROTECT_IN_PROCESS) == 0)
{
DWORD cbPadding;
if((cbIn < RTL_ENCRYPT_MEMORY_SIZE) ||
(cbIn % RTL_ENCRYPT_MEMORY_SIZE))
{
dwRet = ERROR_INVALID_DATA;
goto Ret;
}
dwRet = RpcImpersonateClient(((PCRYPT_SERVER_CONTEXT)pvContext)->hBinding);
if(dwRet == ERROR_SUCCESS)
{
NTSTATUS Status;
Status = RtlDecryptMemory(pbIn,
cbIn,
RTL_ENCRYPT_OPTION_SAME_LOGON);
if(!NT_SUCCESS(Status))
{
dwRet = ERROR_DECRYPTION_FAILED;
}
RevertToSelf();
}
// Remove padding
if(dwRet == ERROR_SUCCESS)
{
cbPadding = pbIn[cbIn - 1];
if((cbPadding > 0) &&
(cbPadding <= cbIn) &&
(cbPadding <= RTL_ENCRYPT_MEMORY_SIZE))
{
cbIn -= cbPadding;
}
else
{
dwRet = ERROR_INVALID_DATA;
}
}
}
// hash & encrypt the data
cbCrypt = cbIn + DEFAULT_BLOCKSIZE_OVERRUN;
if (NULL == (pbCrypt = (PBYTE)SSAlloc(cbCrypt)) )
{
dwRet = ERROR_OUTOFMEMORY;
goto Ret;
}
CopyMemory(pbCrypt, pbIn, cbIn);
// now write data out: size?
*pcbOut = sizeof(GUID) + 2*sizeof(DWORD); // dwVer + guidMK + dwFlags
*pcbOut += sizeof(DWORD) + WSZ_BYTECOUNT(szDataDescr); // data description
*pcbOut += 3*sizeof(DWORD) + sizeof(rgbEncrKey); // EncrAlgID + AlgIDKeySize + cbEncrKey + EncrKey
*pcbOut += sizeof(DWORD) + cbEncrSalt; // Encr salt
*pcbOut += 3*sizeof(DWORD) + sizeof(rgbMACKey); // MACAlgID + AlgIDKeySize + cbMACKey + MACKey
*pcbOut += sizeof(DWORD) + cbCrypt; // size + encrypted data (guess)
*pcbOut += sizeof(DWORD) + A_SHA_DIGEST_LEN; // MAC + MACsize
*ppbOut = (PBYTE)SSAlloc(*pcbOut);
if( *ppbOut == NULL ) {
dwRet = ERROR_OUTOFMEMORY;
goto Ret;
}
ZeroMemory( *ppbOut, *pcbOut );
pbWritePtr = *ppbOut;
////////////////////////////////////////////////////////////////////
// FYI: Data format
// ( Version | guidMKid | dwFlags |
// cbDataDescr | szDataDescr |
//
// dwEncrAlgID | dwEncrAlgKeySize |
// cbEncrKey | EncrKey |
// cbEncrSalt | EncrSalt |
//
// dwMACAlgID | dwMACAlgKeySize |
// cbMACKey | MACKey |
//
// cbEncrData | EncrData |
// cbMAC | MAC )
//
// NOTE: entire buffer from Version through EncrData is included in MAC
////////////////////////////////////////////////////////////////////
// dwVersion
*(DWORD UNALIGNED *)pbWritePtr = MS_BASE_CRYPTPROTECT_VERSION;
pbWritePtr += sizeof(DWORD);
// guid MKid
CopyMemory(pbWritePtr, &guidMK, sizeof(GUID));
pbWritePtr += sizeof(GUID);
// dwFlags -- written out later via pStreamFlagPtr
pStreamFlagPtr = pbWritePtr;
pbWritePtr += sizeof(DWORD);
// cbDataDescr
*(DWORD UNALIGNED *)pbWritePtr = WSZ_BYTECOUNT(szDataDescr);
pbWritePtr += sizeof(DWORD);
// szDataDescr
CopyMemory(pbWritePtr, szDataDescr, WSZ_BYTECOUNT(szDataDescr));
pbWritePtr += WSZ_BYTECOUNT(szDataDescr);
// dwEncrAlgID
*(DWORD UNALIGNED *)pbWritePtr = dwAlgID_Encr_Alg;
pbWritePtr += sizeof(DWORD);
// dwEncrAlgKeySize
*(DWORD UNALIGNED *)pbWritePtr = dwAlgID_Encr_Alg_KeySize;
pbWritePtr += sizeof(DWORD);
// cb EncrKey
*(DWORD UNALIGNED *)pbWritePtr = sizeof(rgbEncrKey);
pbWritePtr += sizeof(DWORD);
// Encr Key
CopyMemory(pbWritePtr, rgbEncrKey, sizeof(rgbEncrKey));
pbWritePtr += sizeof(rgbEncrKey);
// cb Encr salt
*(DWORD UNALIGNED *)pbWritePtr = cbEncrSalt;
pbWritePtr += sizeof(DWORD);
// Encr salt
CopyMemory(pbWritePtr, pbEncrSalt, cbEncrSalt);
pbWritePtr += cbEncrSalt;
// dwMACAlgID
*(DWORD UNALIGNED *)pbWritePtr = dwAlgID_MAC_Alg;
pbWritePtr += sizeof(DWORD);
// dwMACAlgKeySize
*(DWORD UNALIGNED *)pbWritePtr = dwAlgID_MAC_Alg_KeySize;
pbWritePtr += sizeof(DWORD);
// cb MAC key
*(DWORD UNALIGNED *)pbWritePtr = sizeof(rgbMACKey);
pbWritePtr += sizeof(DWORD);
// MAC key
CopyMemory(pbWritePtr, rgbMACKey, sizeof(rgbMACKey));
pbWritePtr += sizeof(rgbMACKey);
// USER GATING: only consider if prompt structure specified
if ( psPrompt )
{
if (psPrompt->cbSize != sizeof(SSCRYPTPROTECTDATA_PROMPTSTRUCT))
{
dwRet = ERROR_INVALID_PARAMETER;
goto Ret;
}
if ((psPrompt->dwPromptFlags & ~(CRYPTPROTECT_PROMPT_ON_PROTECT |
CRYPTPROTECT_PROMPT_ON_UNPROTECT |
CRYPTPROTECT_PROMPT_STRONG |
CRYPTPROTECT_PROMPT_REQUIRE_STRONG)) != 0)
{
dwRet = ERROR_INVALID_PARAMETER;
goto Ret;
}
if ((psPrompt->dwPromptFlags & CRYPTPROTECT_PROMPT_STRONG) &&
(pbOptionalPassword == NULL || cbOptionalPassword == 0)
)
{
dwRet = ERROR_INVALID_PARAMETER;
goto Ret;
}
// UI: only if requested by PROMPT_ON_PROTECT
if ( psPrompt->dwPromptFlags & CRYPTPROTECT_PROMPT_ON_PROTECT )
{
if ( dwFlags & CRYPTPROTECT_UI_FORBIDDEN )
{
dwRet = ERROR_PASSWORD_RESTRICTION;
goto Ret;
}
// UI handled outside service until SAS support added.
dwProtectionFlags |= CRYPTPROTECT_PROMPT_ON_PROTECT;
}
if ( psPrompt->dwPromptFlags & CRYPTPROTECT_PROMPT_ON_UNPROTECT )
dwProtectionFlags |= CRYPTPROTECT_PROMPT_ON_UNPROTECT;
if ( psPrompt->dwPromptFlags & CRYPTPROTECT_PROMPT_STRONG )
dwProtectionFlags |= CRYPTPROTECT_PROMPT_STRONG;
if ( psPrompt->dwPromptFlags & CRYPTPROTECT_PROMPT_REQUIRE_STRONG )
dwProtectionFlags |= CRYPTPROTECT_PROMPT_REQUIRE_STRONG;
}
// update stored protection flags in the stream
*(DWORD UNALIGNED *)pStreamFlagPtr = dwProtectionFlags;
// dansimon recommends that MAC be on encrypted data, such that the
// MAC has no possibility of revealing info about the plaintext.
cbEncrypted = cbIn;
// then Encrypt pbIn
if (!CryptEncrypt(
hKey,
NULL,
TRUE,
0,
pbCrypt,
&cbEncrypted,
cbCrypt))
{
dwRet = GetLastError();
goto Ret;
}
// now cbCrypt is size of encrypted data
cbCrypt = cbEncrypted;
// Encrdata: len
*(DWORD UNALIGNED *)pbWritePtr = cbCrypt;
pbWritePtr += sizeof(DWORD);
// Encrdata: val
CopyMemory(pbWritePtr, pbCrypt, cbCrypt);
pbWritePtr += cbCrypt;
{
// dansimon recommends that MAC be on encrypted data, such that the
// MAC has no possibility of revealing info about the plaintext.
// MAC from start to here
if (!CryptHashData(
hHash,
*ppbOut,
(DWORD)(pbWritePtr - *ppbOut),
0))
{
dwRet = GetLastError();
goto Ret;
}
// cbMAC
pbWritePtr += sizeof(DWORD); // skip cb write; retreive hash val first
// MAC
if (!CryptGetHashParam(
hHash,
HP_HASHVAL,
pbWritePtr,
&cbMACSize,
0))
{
dwRet = GetLastError();
goto Ret;
}
// rewrite cbMAC before MAC
*(DWORD UNALIGNED *)(pbWritePtr - sizeof(DWORD)) = cbMACSize;
// make sure we didn't overstep
SS_ASSERT(cbMACSize <= A_SHA_DIGEST_LEN);
pbWritePtr += cbMACSize;
}
// assert allocation size was sufficient
SS_ASSERT(*ppbOut + *pcbOut >= pbWritePtr);
// reset output size
*pcbOut = (DWORD)(pbWritePtr - *ppbOut);
dwRet = ERROR_SUCCESS;
Ret:
if((dwProtectionFlags & CRYPTPROTECT_AUDIT) ||
(ERROR_SUCCESS != dwRet))
{
WCHAR wszCryptoAlgs[2*MAX_STRING_ALGID_LENGTH + 2];
DWORD i;
i = AlgIDToString(wszCryptoAlgs, dwAlgID_Encr_Alg, dwAlgID_Encr_Alg_KeySize);
wszCryptoAlgs[i++]= L',';
wszCryptoAlgs[i++]= L' ';
AlgIDToString(&wszCryptoAlgs[i], dwAlgID_MAC_Alg, dwAlgID_MAC_Alg_KeySize);
CPSAudit(pServerContext->hToken,
SE_AUDITID_DPAPI_PROTECT,
wszMKGuidString, // Key Identifier
szDataDescr, // Data Description
dwProtectionFlags, // Protected Data Flags
wszCryptoAlgs, // Protection Algorithms
dwRet); // Failure Reason
}
RtlSecureZeroMemory(rgbPwdBuf, sizeof(rgbPwdBuf));
RtlSecureZeroMemory(rgbEncrKey, sizeof(rgbEncrKey));
if(pbMasterKey) {
RtlSecureZeroMemory(pbMasterKey, cbMasterKey);
SSFree(pbMasterKey);
}
if (hKey)
CryptDestroyKey(hKey);
if (hHash)
CryptDestroyHash(hHash);
if (pbCrypt)
SSFree(pbCrypt);
if (pbEncrSalt)
SSFree(pbEncrSalt);
D_DebugLog((DEB_TRACE_API, "SPCryptProtect returned 0x%x\n", dwRet));
return dwRet;
}
DWORD
WINAPI
SPCryptUnprotect(
PVOID pvContext, // server context
PBYTE* ppbOut, // out ptxt data
DWORD* pcbOut, // out ptxt cb
PBYTE pbIn, // in encr data
DWORD cbIn, // in encr cb
LPWSTR* ppszDataDescr, // OPTIONAL
PBYTE pbOptionalEntropy, // OPTIONAL
DWORD cbOptionalEntropy,
PSSCRYPTPROTECTDATA_PROMPTSTRUCT psPrompt, // OPTIONAL, prompting struct
DWORD dwFlags,
BYTE* pbOptionalPassword,
DWORD cbOptionalPassword
)
{
DWORD dwRet;
PBYTE pbReadPtr = pbIn;
LPWSTR szUser = NULL;
GUID guidMK;
WCHAR wszMKGuidString[MAX_GUID_SZ_CHARS];
HCRYPTPROV hVerifyProv;
HCRYPTKEY hKey = NULL;
HCRYPTHASH hHash = NULL;
BYTE rgbEncrKey[KEY_DERIVATION_BUFSIZE];
BYTE rgbMACKey[KEY_DERIVATION_BUFSIZE];
DWORD cbEncr;
PBYTE pbEncrSalt = NULL;
DWORD cbEncrSalt = 0;
DWORD dwEncrAlgID = 0;
DWORD dwEncrAlgKeySize = 0;
DWORD dwMACAlgID = 0;
DWORD dwMACAlgKeySize =0;
DWORD cbEncrKeysize, cbMACKeysize;
DWORD dwProtectionFlags = 0;
BYTE rgbPwdBuf[A_SHA_DIGEST_LEN];
DWORD cbDataDescr;
LPWSTR szDataDescr = NULL;
LPBYTE pbMasterKey = NULL;
DWORD cbMasterKey = 0;
DWORD cbPlaintext;
#if DBG
D_DebugLog((DEB_TRACE_API,"SPCryptUnprotect 0x%x called\n", pvContext));
if(pvContext)
{
PCRYPT_SERVER_CONTEXT pServerContext = (PCRYPT_SERVER_CONTEXT)pvContext;
if(pServerContext->cbSize == sizeof(CRYPT_SERVER_CONTEXT))
{
D_DebugLog((DEB_TRACE_API, " pServerContext->hBinding:%d\n", pServerContext->hBinding));
D_DebugLog((DEB_TRACE_API, " pServerContext->fOverrideToLocalSystem:%d\n", pServerContext->fOverrideToLocalSystem));
D_DebugLog((DEB_TRACE_API, " pServerContext->fImpersonating:%d\n", pServerContext->fImpersonating));
D_DebugLog((DEB_TRACE_API, " pServerContext->hToken:%d\n", pServerContext->hToken));
D_DebugLog((DEB_TRACE_API, " pServerContext->szUserStorageArea:%ls\n", pServerContext->szUserStorageArea));
D_DebugLog((DEB_TRACE_API, " pServerContext->WellKnownAccount:%d\n", pServerContext->WellKnownAccount));
}
}
D_DebugLog((DEB_TRACE_API, " pbOptionalEntropy:0x%x\n", pbOptionalEntropy));
D_DPAPIDumpHexData(DEB_TRACE_BUFFERS, " ", pbOptionalEntropy, cbOptionalEntropy);
D_DebugLog((DEB_TRACE_API, " dwFlags:0x%x\n", dwFlags));
D_DebugLog((DEB_TRACE_API, " pbOptionalPassword:0x%x\n", pbOptionalPassword));
D_DPAPIDumpHexData(DEB_TRACE_BUFFERS, " ", pbOptionalPassword, cbOptionalPassword);
#endif
*ppbOut = NULL;
*pcbOut = 0;
ZeroMemory(&guidMK, sizeof(guidMK));
ZeroMemory(rgbPwdBuf, sizeof(rgbPwdBuf));
DWORD dwDefaultCryptProvType = 0;
DWORD dwAlgID_Encr_Alg = 0;
DWORD dwAlgID_Encr_Alg_KeySize = 0;
DWORD dwAlgID_MAC_Alg = 0;
DWORD dwAlgID_MAC_Alg_KeySize = 0;
//
// If audit is turned on and the following vars are not set with the values
// passed in, then the following would appear in the audit,
// "Unknown 0x0 - 0"
//
dwEncrAlgID = 0;
dwEncrAlgKeySize = 0;
dwMACAlgID = 0;
dwMACAlgKeySize = 0;
wszMKGuidString[0] = 0;
GetDefaultAlgInfo(&dwDefaultCryptProvType,
&dwAlgID_Encr_Alg,
&dwAlgID_Encr_Alg_KeySize,
&dwAlgID_MAC_Alg,
&dwAlgID_MAC_Alg_KeySize);
////////////////////////////////////////////////////////////////////
// FYI: Data format
// ( Version | guidMKid | dwFlags |
// cbDataDescr | szDataDescr |
//
// dwEncrAlgID | dwEncrAlgKeySize |
// cbEncrKey | EncrKey |
// cbEncrSalt | EncrSalt |
//
// dwMACAlgID | dwMACAlgKeySize |
// cbMACKey | MACKey |
//
// cbEncrData | EncrData |
// cbMAC | MAC )
//
// NOTE: entire buffer from ProvHeader through EncrData is included in MAC
////////////////////////////////////////////////////////////////////
// Check for minimum input buffer size.
if(cbIn < sizeof(DWORD) + // Version
sizeof(GUID) + // guidMKid
sizeof(DWORD) + // dwFlags
sizeof(DWORD)) // cbDataDescr
{
dwRet = ERROR_INVALID_DATA;
goto Ret;
}
// version check
if (*(DWORD UNALIGNED *)pbReadPtr != MS_BASE_CRYPTPROTECT_VERSION)
{
dwRet = ERROR_INVALID_DATA;
goto Ret;
}
pbReadPtr += sizeof(DWORD);
cbIn -= sizeof(DWORD);
// guidMKid
CopyMemory(&guidMK, pbReadPtr, sizeof(GUID));
pbReadPtr += sizeof(GUID);
cbIn -= sizeof(GUID);
MyGuidToStringW(&guidMK, wszMKGuidString);
D_DebugLog((DEB_TRACE, "Master key GUID:%ls\n", wszMKGuidString));
// dwFlags during protection
dwProtectionFlags = *(DWORD UNALIGNED *)pbReadPtr;
pbReadPtr += sizeof(DWORD);
cbIn -= sizeof(DWORD);
D_DebugLog((DEB_TRACE, "Protection flags:0x%x\n", dwProtectionFlags));
//
// evaluate original CryptProtectData dwFlags to determine if
// CRYPT_LOCAL_MACHINE set.
//
if( dwProtectionFlags & CRYPTPROTECT_LOCAL_MACHINE ) {
BOOL fOverrideToLocalSystem = TRUE;
CPSOverrideToLocalSystem(
pvContext,
&fOverrideToLocalSystem,
NULL // don't care what previous value was
);
}
if((dwProtectionFlags ^ dwFlags) & CRYPTPROTECT_SYSTEM)
{
//
// Attempted to use decrypt system data as a user, or user data
// with the system flag
dwRet = ERROR_INVALID_DATA;
goto Ret;
}
// cbDataDescr
cbDataDescr = *(DWORD UNALIGNED *)pbReadPtr;
pbReadPtr += sizeof(DWORD);
cbIn -= sizeof(DWORD);
// Check for minimum input buffer size.
if(cbIn < cbDataDescr + // szDataDescr
sizeof(DWORD) + // dwEncrAlgID
sizeof(DWORD) + // dwEncrAlgKeySize
sizeof(DWORD)) // cbEncrKey
{
dwRet = ERROR_INVALID_DATA;
goto Ret;
}
// szDataDescr
szDataDescr = (LPWSTR)pbReadPtr;
pbReadPtr += cbDataDescr;
cbIn -= cbDataDescr;
D_DebugLog((DEB_TRACE, "Description:%ls\n", szDataDescr));
// dwEncrAlgID
dwEncrAlgID = *(DWORD UNALIGNED *)pbReadPtr;
pbReadPtr += sizeof(DWORD);
cbIn -= sizeof(DWORD);
// dwEncrAlgKeySize
dwEncrAlgKeySize = *(DWORD UNALIGNED *)pbReadPtr;
pbReadPtr += sizeof(DWORD);
cbIn -= sizeof(DWORD);
D_DebugLog((DEB_TRACE, "Encrypt alg:0x%x, Size:%d bits\n", dwEncrAlgID, dwEncrAlgKeySize));
// cb Encr key
cbEncrKeysize = *(DWORD UNALIGNED *)pbReadPtr;
pbReadPtr += sizeof(DWORD);
cbIn -= sizeof(DWORD);
if (cbEncrKeysize > sizeof(rgbEncrKey))
{
dwRet = ERROR_INVALID_DATA;
goto Ret;
}
// Check for minimum input buffer size.
if(cbIn < cbEncrKeysize + // EncrKey
sizeof(DWORD)) // cbEncrSalt
{
dwRet = ERROR_INVALID_DATA;
goto Ret;
}
// Encr key
CopyMemory(rgbEncrKey, pbReadPtr, cbEncrKeysize);
pbReadPtr += cbEncrKeysize;
cbIn -= cbEncrKeysize;
// cb Encr salt
cbEncrSalt = *(DWORD UNALIGNED *)pbReadPtr;
pbReadPtr += sizeof(DWORD);
cbIn -= sizeof(DWORD);
// Check for minimum input buffer size.
if(cbIn < cbEncrSalt + // EncrSalt
sizeof(DWORD) + // dwMACAlgID
sizeof(DWORD) + // dwMACAlgKeySize
sizeof(DWORD)) // cbMACKey
{
dwRet = ERROR_INVALID_DATA;
goto Ret;
}
// Encr salt
pbEncrSalt = (PBYTE)SSAlloc(cbEncrSalt);
if( pbEncrSalt == NULL ) {
dwRet = ERROR_OUTOFMEMORY;
goto Ret;
}
CopyMemory(pbEncrSalt, pbReadPtr, cbEncrSalt);
pbReadPtr += cbEncrSalt;
cbIn -= cbEncrSalt;
// dwMACAlgID
dwMACAlgID = *(DWORD UNALIGNED *)pbReadPtr;
pbReadPtr += sizeof(DWORD);
cbIn -= sizeof(DWORD);
// dwMACAlgKeySize
dwMACAlgKeySize = *(DWORD UNALIGNED *)pbReadPtr;
pbReadPtr += sizeof(DWORD);
cbIn -= sizeof(DWORD);
D_DebugLog((DEB_TRACE, "MAC alg:0x%x, Size:%d bits\n", dwMACAlgID, dwMACAlgKeySize));
// MAC key size
cbMACKeysize = *(DWORD UNALIGNED *)pbReadPtr;
pbReadPtr += sizeof(DWORD);
cbIn -= sizeof(DWORD);
if (cbMACKeysize > sizeof(rgbMACKey))
{
dwRet = ERROR_INVALID_DATA;
goto Ret;
}
// Check for minimum input buffer size.
if(cbIn < cbMACKeysize) // cbMACKey
{
dwRet = ERROR_INVALID_DATA;
goto Ret;
}
// MAC key
CopyMemory(rgbMACKey, pbReadPtr, cbMACKeysize);
pbReadPtr += cbMACKeysize;
cbIn -= cbMACKeysize;
if (NULL == (hVerifyProv =
GetCryptProviderHandle(
dwDefaultCryptProvType,
dwEncrAlgID, &dwEncrAlgKeySize,
dwMACAlgID, &dwMACAlgKeySize)) )
{
dwRet = (DWORD)GetLastError();
goto Ret;
}
// USER GATING: when PROMPT_ON_UNPROTECT specified during CryptProtectData
if ( CRYPTPROTECT_PROMPT_ON_UNPROTECT & dwProtectionFlags )
{
if (dwFlags & CRYPTPROTECT_UI_FORBIDDEN)
{
dwRet = ERROR_PASSWORD_RESTRICTION;
goto Ret;
}
if (psPrompt == NULL)
{
dwRet = ERROR_INVALID_PARAMETER;
goto Ret;
}
if (psPrompt->cbSize != sizeof(SSCRYPTPROTECTDATA_PROMPTSTRUCT))
{
dwRet = ERROR_INVALID_PARAMETER;
goto Ret;
}
if ((psPrompt->dwPromptFlags & ~(CRYPTPROTECT_PROMPT_ON_PROTECT |
CRYPTPROTECT_PROMPT_ON_UNPROTECT |
CRYPTPROTECT_PROMPT_STRONG |
CRYPTPROTECT_PROMPT_REQUIRE_STRONG) ) != 0)
{
dwRet = ERROR_INVALID_PARAMETER;
goto Ret;
}
// UI handled outside service until SAS support added.
}
dwRet = GetSpecifiedMasterKey(
pvContext,
&guidMK,
&pbMasterKey,
&cbMasterKey,
TRUE // we do know what master key we want to use
);
if(dwRet != ERROR_SUCCESS)
{
DWORD dwAccount = 0;
DebugLog((DEB_ERROR, "Unable to get specified master key:%ls, error 0x%x\n",
wszMKGuidString, dwRet));
//
// Is this call from one of the service accounts? If so, then attempt to
// obtain the master key using the legacy method.
//
CPSQueryWellKnownAccount(pvContext, &dwAccount);
if((dwAccount == DP_ACCOUNT_LOCAL_SERVICE) ||
(dwAccount == DP_ACCOUNT_NETWORK_SERVICE))
{
DebugLog((DEB_ERROR, "Attempt service account legacy method.\n"));
CPSSetWellKnownAccount(pvContext, 0);
dwRet = GetSpecifiedMasterKey(
pvContext,
&guidMK,
&pbMasterKey,
&cbMasterKey,
TRUE // we do know what master key we want to use
);
CPSSetWellKnownAccount(pvContext, dwAccount);
if(dwRet != ERROR_SUCCESS)
{
DebugLog((DEB_ERROR, "Still unable to get specified master key:%ls, error 0x%x\n",
wszMKGuidString, dwRet));
goto Ret;
}
else
{
DebugLog((DEB_ERROR, "Master key successfully obtained using legacy method.\n"));
}
}
else
{
goto Ret;
}
}
//
// hash pbMasterKey to get rgbPwdBuf
//
FMyPrimitiveSHA( pbMasterKey, cbMasterKey, rgbPwdBuf);
// derive encr key
{
if (!FMyPrimitiveCryptHMAC(
rgbPwdBuf,
sizeof(rgbPwdBuf),
rgbEncrKey,
cbEncrKeysize,
hVerifyProv,
ALGID_DERIVEKEY_HASH,
&hHash))
{
dwRet = GetLastError();
goto Ret;
}
// add password if exists
if (NULL != pbOptionalEntropy)
{
if (!CryptHashData(
hHash,
pbOptionalEntropy,
cbOptionalEntropy,
0))
{
dwRet = GetLastError();
goto Ret;
}
}
// add prompted UI based password if exists.
// will eventually come from SAS
//
if ( NULL != pbOptionalPassword && cbOptionalPassword )
{
if (!CryptHashData(
hHash,
pbOptionalPassword,
cbOptionalPassword,
0))
{
dwRet = GetLastError();
goto Ret;
}
}
if (!CryptDeriveKey(
hVerifyProv,
dwEncrAlgID,
hHash,
((dwEncrAlgKeySize << 16) | CRYPT_CREATE_SALT),
&hKey))
{
dwRet = GetLastError();
goto Ret;
}
CryptDestroyHash(hHash);
hHash = 0;
// USEC -- (US Export Controls)
if (ERROR_SUCCESS != (dwRet =
SetSaltForExportControl(
hKey,
pbEncrSalt,
cbEncrSalt)) )
goto Ret;
}
// derive MAC key
{
if (!FMyPrimitiveCryptHMAC(
rgbPwdBuf,
sizeof(rgbPwdBuf),
rgbMACKey,
cbMACKeysize,
hVerifyProv,
dwMACAlgID,
&hHash))
{
dwRet = GetLastError();
goto Ret;
}
// add password if exists
if (NULL != pbOptionalEntropy)
{
if (!CryptHashData(
hHash,
pbOptionalEntropy,
cbOptionalEntropy,
0))
{
dwRet = GetLastError();
goto Ret;
}
}
// add prompted UI based password if exists.
// will eventually come from SAS
//
if ( NULL != pbOptionalPassword && cbOptionalPassword )
{
if (!CryptHashData(
hHash,
pbOptionalPassword,
cbOptionalPassword,
0))
{
dwRet = GetLastError();
goto Ret;
}
}
// USEC -- (US Export Controls)
// does not apply -- use strong key
}
// Check for minimum input buffer size.
if(cbIn < sizeof(DWORD)) // cbEncrData
{
dwRet = ERROR_INVALID_DATA;
goto Ret;
}
// get encr size
cbEncr = *(DWORD UNALIGNED *)pbReadPtr;
pbReadPtr += sizeof(DWORD);
cbIn -= sizeof(DWORD);
// Check for minimum input buffer size.
if(cbIn < cbEncr + // EncrData
sizeof(DWORD) + // cbMAC
A_SHA_DIGEST_LEN) // MAC
{
dwRet = ERROR_INVALID_DATA;
goto Ret;
}
// dansimon recommends that MAC be on encrypted data, such that the
// MAC has no possibility of revealing info about the plaintext.
// MAC is from start thru encrypted data
if (!CryptHashData(
hHash,
pbIn,
(DWORD) ((pbReadPtr - pbIn) + cbEncr),
0))
{
dwRet = GetLastError();
goto Ret;
}
cbPlaintext = cbEncr;
if ((dwProtectionFlags & CRYPTPROTECT_NO_ENCRYPTION) == 0)
{
if (!CryptDecrypt(
hKey,
NULL, // hHash mattt 9/12/97
TRUE,
0,
pbReadPtr,
&cbPlaintext))
{
dwRet = GetLastError();
if(NTE_BAD_DATA == dwRet)
{
dwRet = ERROR_INVALID_DATA;
}
goto Ret;
}
}
{
BYTE rgbComputedMAC[A_SHA_DIGEST_LEN];
// use MACPtr to skip past decr data,
PBYTE pbMACPtr = pbReadPtr + cbEncr;
DWORD cbMACsize = A_SHA_DIGEST_LEN;
if (!CryptGetHashParam(
hHash,
HP_HASHVAL,
rgbComputedMAC,
&cbMACsize,
0))
{
dwRet = GetLastError();
goto Ret;
}
// chk MAC size
if (*(DWORD UNALIGNED *)pbMACPtr != cbMACsize)
{
dwRet = ERROR_INVALID_DATA;
goto Ret;
}
pbMACPtr += sizeof(DWORD);
// chk MAC
if (0 != memcmp(pbMACPtr, rgbComputedMAC, cbMACsize) )
{
dwRet = ERROR_INVALID_DATA;
goto Ret;
}
}
//
// Write data out, encrypted so that rpc doesn't leave copies
// laying around in plaintext.
//
*pcbOut = cbPlaintext;
if((dwFlags & CRYPTPROTECT_IN_PROCESS) == 0)
{
DWORD cbPadding;
NTSTATUS Status;
cbPadding = RTL_ENCRYPT_MEMORY_SIZE - (*pcbOut) % RTL_ENCRYPT_MEMORY_SIZE;
if(cbPadding == 0)
{
cbPadding += RTL_ENCRYPT_MEMORY_SIZE;
}
*ppbOut = (PBYTE)SSAlloc(*pcbOut + cbPadding);
if(*ppbOut == NULL)
{
*pcbOut = 0;
dwRet = ERROR_OUTOFMEMORY;
goto Ret;
}
CopyMemory(*ppbOut, pbReadPtr, *pcbOut);
FillMemory((*ppbOut) + (*pcbOut), cbPadding, (BYTE)cbPadding);
*pcbOut += cbPadding;
dwRet = RpcImpersonateClient(((PCRYPT_SERVER_CONTEXT)pvContext)->hBinding);
if( dwRet != ERROR_SUCCESS )
{
SSFree(*ppbOut);
*ppbOut = NULL;
*pcbOut = 0;
goto Ret;
}
Status = RtlEncryptMemory(*ppbOut,
*pcbOut,
RTL_ENCRYPT_OPTION_SAME_LOGON);
RevertToSelf();
if(!NT_SUCCESS(Status))
{
SSFree(*ppbOut);
*ppbOut = NULL;
*pcbOut = 0;
dwRet = RtlNtStatusToDosError(Status);
goto Ret;
}
}
else
{
// We're in-process, so don't bother encrypting output buffer.
*ppbOut = (PBYTE)SSAlloc(*pcbOut);
if(*ppbOut == NULL)
{
*pcbOut = 0;
dwRet = ERROR_OUTOFMEMORY;
goto Ret;
}
CopyMemory(*ppbOut, pbReadPtr, *pcbOut);
}
// optional: caller may want data descr
if (ppszDataDescr)
{
*ppszDataDescr = (LPWSTR)SSAlloc(cbDataDescr);
if(*ppszDataDescr == NULL)
{
SSFree(*ppbOut);
*ppbOut = NULL;
*pcbOut = 0;
dwRet = ERROR_OUTOFMEMORY;
goto Ret;
}
CopyMemory(*ppszDataDescr, szDataDescr, cbDataDescr);
}
dwRet = ERROR_SUCCESS;
if(dwFlags & CRYPTPROTECT_VERIFY_PROTECTION )
{
HCRYPTPROV hTestProv = GetCryptProviderHandle( dwDefaultCryptProvType,
dwAlgID_Encr_Alg, &dwAlgID_Encr_Alg_KeySize,
dwAlgID_MAC_Alg, &dwAlgID_MAC_Alg_KeySize);
if(hTestProv)
{
// Verify encryption strengths
// Never downgrade encryption strength, just check if we need
// to upgrade
if((dwAlgID_Encr_Alg_KeySize > dwEncrAlgKeySize) ||
(dwAlgID_MAC_Alg_KeySize > dwMACAlgKeySize))
{
dwRet = CRYPT_I_NEW_PROTECTION_REQUIRED;
}
}
}
Ret:
if((dwProtectionFlags & CRYPTPROTECT_AUDIT) ||
(ERROR_SUCCESS != dwRet))
{
DWORD dwAuditRet = dwRet;
WCHAR wszCryptoAlgs[2*MAX_STRING_ALGID_LENGTH + 2];
DWORD i;
PCRYPT_SERVER_CONTEXT pServerContext = (PCRYPT_SERVER_CONTEXT)pvContext;
i = AlgIDToString(wszCryptoAlgs, dwEncrAlgID, dwEncrAlgKeySize);
wszCryptoAlgs[i++]= L',';
wszCryptoAlgs[i++]= L' ';
AlgIDToString(&wszCryptoAlgs[i], dwMACAlgID, dwMACAlgKeySize);
if(CRYPT_I_NEW_PROTECTION_REQUIRED == dwAuditRet)
{
dwAuditRet = ERROR_SUCCESS;
}
CPSAudit(pServerContext->hToken,
SE_AUDITID_DPAPI_UNPROTECT,
wszMKGuidString, // Key Identifier
szDataDescr, // Data Description
0, // Protected Data Flags
wszCryptoAlgs, // Protection Algorithms
dwAuditRet); // Failure Reason
}
RtlSecureZeroMemory(rgbPwdBuf, sizeof(rgbPwdBuf));
RtlSecureZeroMemory(rgbEncrKey, sizeof(rgbEncrKey));
if(pbMasterKey) {
RtlSecureZeroMemory(pbMasterKey, cbMasterKey);
SSFree(pbMasterKey);
}
if (hKey)
CryptDestroyKey(hKey);
if (hHash)
CryptDestroyHash(hHash);
if (pbEncrSalt)
SSFree(pbEncrSalt);
D_DebugLog((DEB_TRACE_API, "SPCryptUnprotect returned 0x%x\n", dwRet));
return dwRet;
}