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
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;
|
|
}
|
|
|
|
|