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.
 
 
 
 
 
 

1268 lines
33 KiB

/*
File: Secure.cpp
Title: Cryptographic Funcs for Protected Storage
Author: Matt Thomlinson
Date: 11/18/96
Builds up usable cryptographic functionality and exports for
usage by storage module. Since CryptoAPI base provider may call
us, we can't use CryptXXX primitives (circular dependencies
may result). Instead, we build up functions from SHA-1
hash and DES-CBC primitives to Encrypt/Decrypt key blocks, MAC
items, and check password confirmation blocks.
**Master Key Usage**
Encryption is done in a strange way to ease password management
headaches.
// derive a key from the user password, and the password-unique salt
// Salt foils certain (dictionary) attacks on the password
// PWSalt is PASSWORD_SALT_LEN
// UserPW is SHA-1 hash of password excluding zt
// DerivedKey1 is 56-bit DES key
DerivedKey1 = DeriveKey(UserPW | PWSalt);
// using the derived key, decrypt the encrypted master keys
// encrypted master keys are E(master key bits | pwd confirm bits)
MasterBits | PwdConfirmBits = Decrypt( (MasterKey|PwdConfirmKey), DerivedKey1);
// Now we have recovered keys
// MasterKey, PwdConfirmKey are 56-bit DES keys
MasterKey
PwdConfirmKey
// check to make sure this is correct MasterKey by MACing
// the global confirmation string and checking against stored MAC
PwdConfirmMAC = HMAC(g_ConfirmBuf, PwdConfirmKey)
if (0 != memcmp(PwdConfirmMac, rgbStoredMAC, 20)
// wrong pwd!!
We've derived a master key that we can create reproducibly and
be changed without touching every item encrypted by it. That is,
If the user changes the password, we simply need to keep the
MasterBits constant and Encrypt with the new DerivedKey1 to find
the EncryptedMasterBits to write to disk. In this way, we can change
the password without changing the (exceedingly long) master key.
We also have an immediate indication of whether or not the password
used to decrypt the master key is correct.
**Encrypting/MACing a single item**
// assume we have 56-bit DES MasterKey from above
// use master key to decrypt the encrypted item keyblock
// key block holds two keys: Item key and MAC key
ItemKeyBits | MACKeyBits = Decrypt( (ItemKey|MACKey), MasterKey);
// Recovered two DES keys
// ItemKey is 56 bits
// MACKey is 56 bits
ItemKey
MACKey
// MAC the item
ItemMAC = HMAC(pbItemData, MACKey);
// Tack the items' MAC onto the item, and encrypt
EncryptedItem = ENCRYPTED_DATA_VER | Encrypt(pbItemData | ItemMAC, ItemKey);
We've succeeded in encrypting and MACing an item using the single DES
MasterKey from above. The item is both privacy and integrity protected.
**Derive Key**
**HMAC**
Above we've skimmed over the definition of key derivation and HMAC.
See primitiv.cpp for a description of how this primitive
functionality is implemented.
// Format of the encrypted item data stream:
// version | size(keyblk) | keyblk | size(encryptedblk) | Encrypted{ size(data) | data | size(MAC) | MAC}
*/
#include <pch.cpp>
#pragma hdrstop
#include "storage.h"
// from des.h
#define DES_KEYLEN 8
// MAC buffer we use to check correct decryption
static BYTE g_rgbConfirmBuf[] = "(c) 1996 Microsoft, All Rights Reserved";
// Data Version
//
// #define ENCRYPTED_DATA_VER 0x01
// 6-12-97: version incremented. Previous versions
// should use MyOldPrimitiveHMAC -- different MAC attached to items
#define ENCRYPTED_DATA_VER 0x02
// MK Version
//
// #define ENCRYPTED_MASTERKEY_VER 0x01
// 6-12-97: version incremented. Previous versions
// should use MyOldPrimitiveHMAC -- different MAC attached to items
// #define ENCRYPTED_MASTERKEY_VER 0x02
// 5-3-99: version incremented. Previous versions
// should use sizeof(rgbpwd)
#define ENCRYPTED_MASTERKEY_VER 0x03
// given pwd, salt, and ptr to master key buffer,
// decrypts and checks MAC on master key
BOOL FMyDecryptMK(
BYTE rgbSalt[],
DWORD cbSalt,
BYTE rgbPwd[A_SHA_DIGEST_LEN],
BYTE rgbConfirm[A_SHA_DIGEST_LEN],
PBYTE* ppbMK,
DWORD* pcbMK)
{
BOOL fResetSecurityState;
return FMyDecryptMKEx(
rgbSalt,
cbSalt,
rgbPwd,
rgbConfirm,
ppbMK,
pcbMK,
&fResetSecurityState
);
}
BOOL
FMyDecryptMKEx(
BYTE rgbSalt[],
DWORD cbSalt,
BYTE rgbPwd[A_SHA_DIGEST_LEN],
BYTE rgbConfirm[A_SHA_DIGEST_LEN],
PBYTE* ppbMK,
DWORD* pcbMK,
BOOL *pfResetSecurityState
)
{
BOOL fRet = FALSE;
DESKEY sDerivedKey1;
DESKEY sConfirmKey;
DWORD dwMKVersion;
PBYTE pTemp;
BYTE rgbHMACResult[A_SHA_DIGEST_LEN];
// version check!!
dwMKVersion = *(DWORD*)*ppbMK;
if (ENCRYPTED_MASTERKEY_VER < dwMKVersion)
goto Ret;
if( dwMKVersion < 0x03 ) {
*pfResetSecurityState = TRUE;
// DK1 = DeriveKey(SHA(pw), Salt)
if (!FMyPrimitiveDeriveKey(
rgbSalt,
cbSalt,
rgbPwd,
sizeof(rgbPwd),
&sDerivedKey1))
goto Ret;
} else {
*pfResetSecurityState = FALSE;
// DK1 = DeriveKey(SHA(pw), Salt)
if (!FMyPrimitiveDeriveKey(
rgbSalt,
cbSalt,
rgbPwd,
A_SHA_DIGEST_LEN,
&sDerivedKey1))
goto Ret;
}
*pcbMK -= sizeof(DWORD);
if (!(*pcbMK)) {
// Paranoid
goto Ret;
}
MoveMemory(*ppbMK, *ppbMK + sizeof(DWORD), *pcbMK); // shift data left 1 dw, splat version
pTemp = (PBYTE)SSReAlloc(*ppbMK, *pcbMK);
if (pTemp == NULL) { // check allocation
goto Ret;
}
*ppbMK = pTemp;
// Decrypt MK bits
if (!FMyPrimitiveDESDecrypt(
*ppbMK,
pcbMK,
sDerivedKey1))
goto Ret;
// assumes is at least 2*DES_KEYLEN bytes
if (*pcbMK != 2*DES_KEYLEN)
goto Ret;
if (!FMyMakeDESKey(
&sConfirmKey, // out
*ppbMK + DES_KEYLEN)) // in
goto Ret;
if (dwMKVersion == 0x01)
{
// items created with tag 0x01 used different HMACing algorithm
if (!FMyOldPrimitiveHMAC(
sConfirmKey,
g_rgbConfirmBuf,
sizeof(g_rgbConfirmBuf),
rgbHMACResult))
goto Ret;
}
else
{
if (!FMyPrimitiveHMAC(
sConfirmKey,
g_rgbConfirmBuf,
sizeof(g_rgbConfirmBuf),
rgbHMACResult))
goto Ret;
}
if (0 != memcmp(rgbHMACResult, rgbConfirm, A_SHA_DIGEST_LEN))
goto Ret;
fRet = TRUE;
Ret:
return fRet;
}
// retrieve key block and derive Item/MAC keys using MK
BOOL FMyDecryptKeyBlock(
LPCWSTR szUser,
LPCWSTR szMasterKey,
BYTE rgbPwd[A_SHA_DIGEST_LEN],
PBYTE pbKeyBlock,
DWORD cbKeyBlock,
DESKEY* psItemKey,
DESKEY* psMacKey)
{
BOOL fRet = FALSE;
DESKEY sMK;
BYTE rgbSalt[PASSWORD_SALT_LEN];
BYTE rgbConfirm[A_SHA_DIGEST_LEN];
PBYTE pbMK = NULL;
DWORD cbMK;
if (!FBPGetSecurityState(
szUser,
szMasterKey,
rgbSalt,
sizeof(rgbSalt),
rgbConfirm,
sizeof(rgbConfirm),
&pbMK,
&cbMK))
return FALSE;
// unwrap master key
if (!FMyDecryptMK(
rgbSalt,
sizeof(rgbSalt),
rgbPwd,
rgbConfirm,
&pbMK,
&cbMK
))
goto Ret;
// assumes pbMK is at least 2*DES_KEYLEN bytes
if (cbMK != 2*DES_KEYLEN)
goto Ret;
if (!FMyMakeDESKey(
&sMK, // out
pbMK)) // in
goto Ret;
// use MK to decrypt key block
if (!FMyPrimitiveDESDecrypt(
pbKeyBlock,
&cbKeyBlock,
sMK))
goto Ret;
// fill in ItemKey, MacKey from decrypted key block
if (cbKeyBlock != 2*DES_KEYLEN)
goto Ret;
// assumes pbKeyBlock is at least 2*DES_KEYLEN bytes
if (!FMyMakeDESKey(
psItemKey, // out
pbKeyBlock)) // in
goto Ret;
if (!FMyMakeDESKey(
psMacKey, // out
pbKeyBlock + DES_KEYLEN)) // in
goto Ret;
fRet = TRUE;
Ret:
if(pbMK != NULL) {
RtlSecureZeroMemory(pbMK, cbMK); // sfield: zero it
SSFree(pbMK); // sfield: fix illusive memory leak
}
return fRet;
}
// given encrypted data and password, decrypt and check MAC on data
BOOL FProvDecryptData(
LPCWSTR szUser, // in
LPCWSTR szMasterKey, // in
BYTE rgbPwd[A_SHA_DIGEST_LEN], // in
PBYTE* ppbMyData, // in out
DWORD* pcbMyData) // in out
{
BOOL fRet = FALSE;
DESKEY sItemKey;
DESKEY sMacKey;
BYTE rgbHMAC[A_SHA_DIGEST_LEN];
DWORD dwDataVer;
PBYTE pTemp;
// pointers to teardown stream
PBYTE pbCurPtr = *ppbMyData;
PBYTE pbSecureData;
DWORD cbSecureData;
PBYTE pbInlineKeyBlock;
DWORD cbInlineKeyBlock;
PBYTE pbDecrypted;
DWORD cbDecrypted;
PBYTE pbMAC;
DWORD cbMAC;
// ENCRYPTED ITEM DATA FORMAT:
// version | size(keyblk) | keyblk | size(encryptedblk) | Encrypted{ size(data) | data | size(MAC) | MAC}
// version check -- only handle V1 data for now
dwDataVer = *(DWORD*)pbCurPtr;
if (ENCRYPTED_DATA_VER < dwDataVer)
goto Ret;
pbCurPtr += sizeof(DWORD);
// pointers to key block
cbInlineKeyBlock = *(DWORD UNALIGNED *)pbCurPtr; // keyblock size
pbCurPtr += sizeof(DWORD); // fwd past size
pbInlineKeyBlock = pbCurPtr; // points to key block
pbCurPtr += cbInlineKeyBlock; // fwd past data
// pointers to secure data
cbSecureData = *(DWORD UNALIGNED *)pbCurPtr; // secure data size member
pbCurPtr += sizeof(DWORD); // fwd past size
pbSecureData = pbCurPtr; // points to secure data
// retrieve key block using MK, etc
if (!FMyDecryptKeyBlock(
szUser,
szMasterKey,
rgbPwd,
pbInlineKeyBlock,
cbInlineKeyBlock,
&sItemKey,
&sMacKey))
goto Ret;
// keys derived, now recover data inplace
if (!FMyPrimitiveDESDecrypt(
pbSecureData,
&cbSecureData,
sItemKey))
goto Ret;
cbDecrypted = *(DWORD UNALIGNED *)pbCurPtr; // plaintext size
pbCurPtr += sizeof(DWORD); // fwd past size
pbDecrypted = pbCurPtr; // points to plaintext
pbCurPtr += cbDecrypted; // fwd past data
// pointers to HMAC
cbMAC = *(DWORD UNALIGNED *)pbCurPtr; // MAC size member
pbCurPtr += sizeof(DWORD); // fwd past size
pbMAC = pbCurPtr; // points to MAC
pbCurPtr += cbMAC; // fwd past data
if (A_SHA_DIGEST_LEN != cbMAC) // verify HMAC size member
goto Ret;
// chk MAC
// Compute HMAC over plaintext data
if (dwDataVer == 0x01)
{
// version 0x1 used different HMAC code
if (!FMyOldPrimitiveHMAC(
sMacKey,
pbDecrypted,
cbDecrypted,
rgbHMAC))
goto Ret;
}
else
{
if (!FMyPrimitiveHMAC(
sMacKey,
pbDecrypted,
cbDecrypted,
rgbHMAC))
goto Ret;
}
// now compare against HMAC in tail of msg
if (0 != memcmp(pbMAC, rgbHMAC, A_SHA_DIGEST_LEN))
goto Ret;
// if everything went well, return secure data (shift to far left, realloc)
MoveMemory(*ppbMyData, pbDecrypted, cbDecrypted);
pTemp = (PBYTE)SSReAlloc(*ppbMyData, cbDecrypted);
if (pTemp == NULL) // check allocation, Caller will free *ppbMyData
goto Ret;
*ppbMyData = pTemp;
*pcbMyData = cbDecrypted;
fRet = TRUE;
Ret:
// TODO free ppbMyData on failure?
return fRet;
}
// given pwd, salt, and Master Key buffer, MACs and Encrypts Master Key buffer
BOOL FMyEncryptMK(
BYTE rgbSalt[],
DWORD cbSalt,
BYTE rgbPwd[A_SHA_DIGEST_LEN],
BYTE rgbConfirm[A_SHA_DIGEST_LEN],
PBYTE* ppbMK,
DWORD* pcbMK)
{
BOOL fRet = FALSE;
DESKEY sDerivedKey1;
DESKEY sConfirmKey;
PBYTE pTemp;
// assumes pbKeyBlock is at least 2*DES_KEYLEN bytes
if (*pcbMK != 2*DES_KEYLEN)
goto Ret;
// confirmation key is 2nd in buffer
if (!FMyMakeDESKey(
&sConfirmKey, // out
*ppbMK + DES_KEYLEN)) // in
goto Ret;
if (!FMyPrimitiveHMAC(
sConfirmKey,
g_rgbConfirmBuf,
sizeof(g_rgbConfirmBuf),
rgbConfirm))
goto Ret;
// DK1 = DeriveKey(SHA(pw), Salt)
if (!FMyPrimitiveDeriveKey(
rgbSalt,
cbSalt,
rgbPwd,
A_SHA_DIGEST_LEN, ///sizeof(rgbPwd),
&sDerivedKey1))
goto Ret;
// Encrypt MK w/ DK1, return
if (!FMyPrimitiveDESEncrypt(
ppbMK,
pcbMK,
sDerivedKey1))
goto Ret;
// Mash version onto front!!
pTemp = (PBYTE)SSReAlloc(*ppbMK, *pcbMK+sizeof(DWORD)); // realloc bigger for ver
if (pTemp == NULL) // check allocation
goto Ret;
*ppbMK = pTemp;
MoveMemory(*ppbMK+sizeof(DWORD), *ppbMK, *pcbMK); // move data 1 dw right
*pcbMK += sizeof(DWORD); // inc size
*(DWORD*)(*ppbMK) = (DWORD)ENCRYPTED_MASTERKEY_VER; // whack version in there!
fRet = TRUE;
Ret:
return fRet;
}
// returns a new key block encrypted with master key
// creates and stores master key state if none exists
BOOL FMyEncryptKeyBlock(
LPCWSTR szUser,
LPCWSTR szMasterKey,
BYTE rgbPwd[A_SHA_DIGEST_LEN],
PBYTE* ppbKeyBlock,
DWORD* pcbKeyBlock,
DESKEY* psItemKey,
DESKEY* psMacKey)
{
BOOL fRet = FALSE;
*ppbKeyBlock = NULL;
BYTE rgbSalt[PASSWORD_SALT_LEN];
BYTE rgbConfirm[A_SHA_DIGEST_LEN];
PBYTE pbMK = NULL;
DWORD cbMK;
PBYTE pbTmp = NULL;
DWORD cbTmp;
DESKEY sMK;
// gen a random key block: 2 keys
*pcbKeyBlock = 2*DES_KEYLEN;
*ppbKeyBlock = (PBYTE) SSAlloc(*pcbKeyBlock + DES_BLOCKLEN); // performance fudge factor (realloc)
if (*ppbKeyBlock == NULL) // check allocation
goto Ret;
if (!RtlGenRandom(*ppbKeyBlock, *pcbKeyBlock))
goto Ret;
// INSERT: FRENCH GOVT. THOUGHT CONTROL CODE
if (! FIsEncryptionPermitted())
{
// Protected Storage addition, 5/27/97
// If encryption is not allowed, pretend faulty
// RNG generated encryption key { 6d 8a 88 6a 4e aa 37 a8 }
SS_ASSERT(DES_KEYLEN == sizeof(DWORD)*2);
*(DWORD*)(*ppbKeyBlock) = 0x6d8a886a;
*(DWORD*)(*ppbKeyBlock + sizeof(DWORD)) = 0x4eaa37a8;
// PS: Remind me not to move to FRANCE
}
// assumes pbKeyBlock is at least 2*DES_KEYLEN bytes
SS_ASSERT(*pcbKeyBlock == 2*DES_KEYLEN);
if (!FMyMakeDESKey(
psItemKey, // out
*ppbKeyBlock)) // in
goto Ret;
if (!FMyMakeDESKey(
psMacKey, // out
*ppbKeyBlock + DES_KEYLEN)) // in
goto Ret;
// first derive a key from PW
if (FBPGetSecurityState(
szUser,
szMasterKey,
rgbSalt,
sizeof(rgbSalt),
rgbConfirm,
sizeof(rgbConfirm),
&pbMK,
&cbMK))
{
// unwrap master key
if (!FMyDecryptMK(
rgbSalt,
sizeof(rgbSalt),
rgbPwd,
rgbConfirm,
&pbMK,
&cbMK))
goto Ret;
// done, have MK unwrapped.
}
else
{
// if we couldn't retrieve state, assume we must generate it
if (!RtlGenRandom(rgbSalt, PASSWORD_SALT_LEN))
goto Ret;
cbMK = 2*DES_KEYSIZE;
pbMK = (PBYTE)SSAlloc(cbMK + DES_BLOCKLEN); // performance fudge factor (realloc)
if (pbMK == NULL) // check allocation
goto Ret;
if (!RtlGenRandom(pbMK, cbMK))
goto Ret;
// this is final MK: encrypt a copy
cbTmp = cbMK;
pbTmp = (PBYTE)SSAlloc(cbTmp);
if (pbTmp == NULL) // check allocation
goto Ret;
CopyMemory(pbTmp, pbMK, cbMK);
// now wrap MK up and stuff in registry
if (!FMyEncryptMK(
rgbSalt,
sizeof(rgbSalt),
rgbPwd,
rgbConfirm,
&pbTmp,
&cbTmp))
goto Ret;
if (!FBPSetSecurityState(
szUser,
szMasterKey,
rgbSalt,
PASSWORD_SALT_LEN,
rgbConfirm,
sizeof(rgbConfirm),
pbTmp,
cbTmp))
goto Ret;
}
if (cbMK != 2*DES_KEYSIZE)
goto Ret;
if (!FMyMakeDESKey(
&sMK, // out
pbMK)) // in
goto Ret;
if (*pcbKeyBlock != 2*DES_KEYLEN)
goto Ret;
if (!FMyPrimitiveDESEncrypt(
ppbKeyBlock,
pcbKeyBlock,
sMK))
goto Ret;
fRet = TRUE;
Ret:
if (!fRet)
{
if (*ppbKeyBlock) {
SSFree(*ppbKeyBlock);
*ppbKeyBlock = NULL;
}
}
if (pbMK) {
RtlSecureZeroMemory(pbMK, cbMK);
SSFree(pbMK);
}
if (pbTmp) {
RtlSecureZeroMemory(pbTmp, cbTmp); // sfield: zero memory
SSFree(pbTmp);
}
return fRet;
}
// given data, will generate a key block and
// return encrypt/mac'd data
BOOL FProvEncryptData(
LPCWSTR szUser, // in
LPCWSTR szMasterKey, // in
BYTE rgbPwd[A_SHA_DIGEST_LEN], // in
PBYTE* ppbMyData, // in out
DWORD* pcbMyData) // in out
{
BOOL fRet = FALSE;
DESKEY sItemKey;
DESKEY sMacKey;
BYTE rgbHMAC[A_SHA_DIGEST_LEN];
// helpful pointers
PBYTE pbCurPtr = *ppbMyData;
PBYTE pbKeyBlock = NULL;
DWORD cbKeyBlock = 0;
DWORD cbDataSize;
// return an item key, mac key
// store in an encrypted key block using MK, etc.
if (!FMyEncryptKeyBlock(
szUser,
szMasterKey,
rgbPwd,
&pbKeyBlock,
&cbKeyBlock,
&sItemKey,
&sMacKey))
goto Ret;
// now secure data
// Compute HMAC
if (!FMyPrimitiveHMAC(sMacKey, *ppbMyData, *pcbMyData, rgbHMAC))
goto Ret;
// DATA FORMAT:
// version | size(keyblk) | keyblk | size(encryptedblk) | Encrypted{ size(data) | data | size(MAC) | MAC}
// lengthen data seg by data size member, MAC and MAC size member
cbDataSize = *pcbMyData; // save current size
*pcbMyData += A_SHA_DIGEST_LEN + 2*sizeof(DWORD); // sizeof(data), MAC, sizeof(MAC)
pbCurPtr = (PBYTE)SSReAlloc(*ppbMyData, *pcbMyData);
if (pbCurPtr == NULL) // check allocation
goto Ret;
*ppbMyData = pbCurPtr;
// size, data
MoveMemory(pbCurPtr+sizeof(DWORD), pbCurPtr, cbDataSize); // shift right data for size insertion
*(DWORD UNALIGNED *)pbCurPtr = cbDataSize; // size of data
pbCurPtr += sizeof(DWORD); // fwd past size
pbCurPtr += cbDataSize; // fwd past data
// size, MAC
*(DWORD UNALIGNED *)pbCurPtr = A_SHA_DIGEST_LEN; // size of MAC
pbCurPtr += sizeof(DWORD); // fwd past size
CopyMemory(pbCurPtr, rgbHMAC, A_SHA_DIGEST_LEN); // MAC
pbCurPtr += A_SHA_DIGEST_LEN; // fwd past MAC
if (!FMyPrimitiveDESEncrypt(
ppbMyData, // in out
pcbMyData, // in out
sItemKey))
goto Ret;
cbDataSize = *pcbMyData; // save current size
*pcbMyData += 3*sizeof(DWORD) + cbKeyBlock; // ver, sizeof(keyblk), keyblk, sizeof(encrdata)
pbCurPtr = (PBYTE)SSReAlloc(*ppbMyData, *pcbMyData);
if (pbCurPtr == NULL) // check allocation
goto Ret;
*ppbMyData = pbCurPtr;
// shift right data for size, keyblk insertions
MoveMemory(pbCurPtr + 3*sizeof(DWORD) + cbKeyBlock, pbCurPtr, cbDataSize);
// throw version tag in front
*(DWORD UNALIGNED *)pbCurPtr = (DWORD)ENCRYPTED_DATA_VER;
pbCurPtr += sizeof(DWORD);
// insert keyblock
*(DWORD UNALIGNED *)pbCurPtr = cbKeyBlock; // size of data
pbCurPtr += sizeof(DWORD); // fwd past size
CopyMemory(pbCurPtr, pbKeyBlock, cbKeyBlock); // data
pbCurPtr += cbKeyBlock; // fwd past data
// insert sizeof encrypted blob
*(DWORD UNALIGNED *)pbCurPtr = cbDataSize; // size of data
fRet = TRUE;
Ret:
RtlSecureZeroMemory(&sItemKey, sizeof(DESKEY));
RtlSecureZeroMemory(&sMacKey, sizeof(DESKEY));
if (pbKeyBlock)
SSFree(pbKeyBlock);
return fRet;
}
// given password, and master key, will decrypt and
// verify MAC on master key
BOOL FCheckPWConfirm(
LPCWSTR szUser, // in
LPCWSTR szMasterKey, // in
BYTE rgbPwd[A_SHA_DIGEST_LEN]) // in
{
BOOL fRet = FALSE;
BYTE rgbSalt[PASSWORD_SALT_LEN];
BYTE rgbConfirm[A_SHA_DIGEST_LEN];
PBYTE pbMK = NULL;
DWORD cbMK;
// confirm is just get state and attempt MK decrypt
if (FBPGetSecurityState(
szUser,
szMasterKey,
rgbSalt,
sizeof(rgbSalt),
rgbConfirm,
sizeof(rgbConfirm),
&pbMK,
&cbMK))
{
BOOL fResetSecurityState;
// found state; is pwd correct?
if (!FMyDecryptMKEx(
rgbSalt,
sizeof(rgbSalt),
rgbPwd,
rgbConfirm,
&pbMK,
&cbMK,
&fResetSecurityState
))
goto Ret;
if( fResetSecurityState )
{
// now wrap MK up and stuff in registry
if(FMyEncryptMK(
rgbSalt,
sizeof(rgbSalt),
rgbPwd,
rgbConfirm,
&pbMK,
&cbMK
)) {
if (FBPSetSecurityState(
szUser,
szMasterKey,
rgbSalt,
sizeof(rgbSalt),
rgbConfirm,
sizeof(rgbConfirm),
pbMK,
cbMK
))
{
// found state; is pwd correct?
if (!FMyDecryptMKEx(
rgbSalt,
sizeof(rgbSalt),
rgbPwd,
rgbConfirm,
&pbMK,
&cbMK,
&fResetSecurityState
)) {
OutputDebugString(TEXT("fail to dec\n"));
goto Ret;
}
}
}
} // reset security state.
}
else
{
// didn't find state; create it
// if we couldn't retrieve state, assume we must generate it
if (!RtlGenRandom(rgbSalt, PASSWORD_SALT_LEN))
goto Ret;
cbMK = 2*DES_KEYLEN;
pbMK = (PBYTE)SSAlloc(cbMK);
if (pbMK == NULL) // check allocation
goto Ret;
if (!RtlGenRandom(pbMK, cbMK))
goto Ret;
// now wrap MK up and stuff in registry
if (!FMyEncryptMK(
rgbSalt,
sizeof(rgbSalt),
rgbPwd,
rgbConfirm,
&pbMK,
&cbMK))
goto Ret;
if (!FBPSetSecurityState(
szUser,
szMasterKey,
rgbSalt,
PASSWORD_SALT_LEN,
rgbConfirm,
sizeof(rgbConfirm),
pbMK,
cbMK))
goto Ret;
}
fRet = TRUE;
Ret:
if (pbMK) {
RtlSecureZeroMemory(pbMK, cbMK); // sfield: zero memory
SSFree(pbMK);
}
return fRet;
}
// callback for changing a password. On password change,
// MasterKey is decrypted and re-encrypted
BOOL FPasswordChangeNotify(
LPCWSTR szUser, // in
LPCWSTR szPasswordName, // in
BYTE rgbOldPwd[A_SHA_DIGEST_LEN], // in
DWORD cbOldPwd, // in
BYTE rgbNewPwd[A_SHA_DIGEST_LEN], // in
DWORD cbNewPwd) // in
{
// allows unattended pw change (callback from svr)
BOOL fRet = FALSE;
BYTE rgbSalt[PASSWORD_SALT_LEN];
BYTE rgbConfirm[A_SHA_DIGEST_LEN];
PBYTE pbMK = NULL;
DWORD cbMK;
BOOL fNewPassword = (cbOldPwd == 0);
// can't modify a non-user changable pw
// if (!FIsUserMasterKey(szPasswordName))
// goto Ret;
if (cbNewPwd != A_SHA_DIGEST_LEN)
goto Ret;
// ensure old pwd exists
if (!FBPGetSecurityState(
szUser,
szPasswordName,
rgbSalt,
sizeof(rgbSalt),
rgbConfirm,
sizeof(rgbConfirm),
&pbMK,
&cbMK))
{
// couldn't retreive old PW, create a new one
if (!FCheckPWConfirm(
szUser,
szPasswordName,
rgbNewPwd))
goto Ret;
fRet = TRUE;
goto Ret;
}
else
{
// state was retrieved
if (fNewPassword)
{
SetLastError((DWORD)PST_E_ITEM_EXISTS);
goto Ret;
}
}
// old pwd retrieved -- time to update
if (!FMyDecryptMK(
rgbSalt,
sizeof(rgbSalt),
rgbOldPwd,
rgbConfirm,
&pbMK,
&cbMK))
{
SetLastError((DWORD)PST_E_WRONG_PASSWORD);
goto Ret;
}
// MK is naked here
// rewrap and save state
if (!FMyEncryptMK(
rgbSalt,
sizeof(rgbSalt),
rgbNewPwd,
rgbConfirm,
&pbMK,
&cbMK))
goto Ret;
if (!FBPSetSecurityState(
szUser,
szPasswordName,
rgbSalt,
sizeof(rgbSalt),
rgbConfirm,
sizeof(rgbConfirm),
pbMK,
cbMK))
goto Ret;
fRet = TRUE;
Ret:
if (pbMK) {
RtlSecureZeroMemory(pbMK, cbMK);
SSFree(pbMK);
}
return fRet;
}
BOOL FHMACGeographicallySensitiveData(
LPCWSTR szUser, // in
LPCWSTR szPasswordName, // in
DWORD dwHMACVersion, // in
BYTE rgbPwd[A_SHA_DIGEST_LEN], // in
const GUID* pguidType, // in
const GUID* pguidSubtype, // in
LPCWSTR szItem, // in: may be NULL
PBYTE pbBuf, // in
DWORD cbBuf, // in
BYTE rgbHMAC[A_SHA_DIGEST_LEN]) // out
{
BOOL fRet = FALSE;
// helpful pointer
PBYTE pbCurrent;
PBYTE pbKeyBlock = NULL;
DWORD cbKeyBlock;
DESKEY sBogusKey;
DESKEY sMacKey;
DWORD cbTmp = (DWORD)(cbBuf + 2*sizeof(GUID) + WSZ_BYTECOUNT(szItem));
PBYTE pbTmp = (PBYTE)SSAlloc(cbTmp);
if (pbTmp == NULL) // check allocation
goto Ret;
// helpful pointer
pbCurrent = pbTmp;
// snag the MAC key
if (!FGetInternalMACKey(szUser, &pbKeyBlock, &cbKeyBlock))
{
// create a key block
if (!FMyEncryptKeyBlock(
szUser,
szPasswordName,
rgbPwd,
&pbKeyBlock,
&cbKeyBlock,
&sBogusKey,
&sMacKey))
goto Ret;
if (!FSetInternalMACKey(szUser, pbKeyBlock, cbKeyBlock))
goto Ret;
}
else
{
// key already exists; get it
if (!FMyDecryptKeyBlock(
szUser,
szPasswordName,
rgbPwd,
pbKeyBlock,
cbKeyBlock,
&sBogusKey,
&sMacKey))
goto Ret;
}
// HMAC format:
// HMAC( guidType | guidSubtype | szItemName | pbData )
// copy type
CopyMemory(pbCurrent, pguidType, sizeof(GUID));
pbCurrent += sizeof(GUID);
// copy subtype
CopyMemory(pbCurrent, pguidSubtype, sizeof(GUID));
pbCurrent += sizeof(GUID);
// copy item name
CopyMemory(pbCurrent, szItem, WSZ_BYTECOUNT(szItem));
pbCurrent += WSZ_BYTECOUNT(szItem);
// copy actual data
CopyMemory(pbCurrent, pbBuf, cbBuf);
if (dwHMACVersion == OLD_HMAC_VERSION)
{
// now do HMAC on this
if (!FMyOldPrimitiveHMAC(
sMacKey,
pbTmp,
cbTmp,
rgbHMAC))
goto Ret;
}
else
{
// now do HMAC on this
if (!FMyPrimitiveHMAC(
sMacKey,
pbTmp,
cbTmp,
rgbHMAC))
goto Ret;
}
fRet = TRUE;
Ret:
if (pbTmp)
SSFree(pbTmp);
if (pbKeyBlock)
SSFree(pbKeyBlock);
return fRet;
}
// lifted (nearly) directly from RSABase ntagum.c
// do locale check once
static BOOL g_fEncryptionIsPermitted;
static BOOL g_fIKnowEncryptionPermitted = FALSE;
BOOL FIsEncryptionPermitted()
/*++
Routine Description:
This routine checks whether encryption is getting the system default
LCID and checking whether the country code is CTRY_FRANCE.
Arguments:
none
Return Value:
TRUE - encryption is permitted
FALSE - encryption is not permitted
--*/
{
LCID DefaultLcid;
CHAR CountryCode[10];
ULONG CountryValue;
if (!g_fIKnowEncryptionPermitted)
{
// assume okay (unless found otherwise)
g_fEncryptionIsPermitted = TRUE;
DefaultLcid = GetSystemDefaultLCID();
//
// Check if the default language is Standard French
//
if (LANGIDFROMLCID(DefaultLcid) == 0x40c)
g_fEncryptionIsPermitted = FALSE;
//
// Check if the users's country is set to FRANCE
//
if (GetLocaleInfoA(DefaultLcid,LOCALE_ICOUNTRY,CountryCode,10) == 0)
g_fEncryptionIsPermitted = FALSE;
/*
CountryValue = (ULONG) atol(CountryCode);
if (CountryValue == CTRY_FRANCE)
return(FALSE);
*/
//
// begin remove dependency on atol and msvcrt
//
// from winnls.h:
// #define CTRY_FRANCE 33 // France
SS_ASSERT(CTRY_FRANCE == 33);
if (0 == lstrcmpA(CountryCode, "33"))
g_fEncryptionIsPermitted = FALSE;
//
// end remove dependency on atol and msvcrt
//
g_fIKnowEncryptionPermitted = TRUE;
}
return g_fEncryptionIsPermitted;
}