2025-04-27 07:49:33 -04:00

1435 lines
35 KiB
C++

/*++
Copyright (c) 1996, 1997 Microsoft Corporation
Module Name:
keybckup.cpp
Abstract:
This module contains routines associated with client side Key Backup
operations.
Author:
Scott Field (sfield) 16-Sep-97
--*/
#include <pch.cpp>
#pragma hdrstop
extern "C" {
#include <dsgetdc.h>
#include <msaudite.h>
}
typedef struct _WZR_RPC_BINDING_LIST
{
LPCWSTR pszProtSeq;
LPCWSTR pszEndpoint;
} WZR_RPC_BINDING_LIST;
WZR_RPC_BINDING_LIST g_awzrBackupBindingList[] =
{
{ DPAPI_LOCAL_PROT_SEQ, DPAPI_LOCAL_ENDPOINT },
{ DPAPI_BACKUP_PROT_SEQ, DPAPI_BACKUP_ENDPOINT},
{ DPAPI_LEGACY_BACKUP_PROT_SEQ, DPAPI_LEGACY_BACKUP_ENDPOINT}
};
DWORD g_cwzrBackupBindingList = sizeof(g_awzrBackupBindingList)/sizeof(g_awzrBackupBindingList[0]);
DWORD
WINAPI
CPSGetDomainControllerName(
IN OUT LPWSTR wszDomainControllerName,
IN OUT DWORD *pcchDomainControllerName,
IN BOOL fRediscover
);
BOOL
GetDomainControllerNameByToken(
IN HANDLE hToken,
IN OUT LPWSTR wszDomainControllerName,
IN OUT PDWORD pcchDomainControllerName,
IN BOOL fRediscover
);
static const GUID guidRetrieve = BACKUPKEY_RETRIEVE_BACKUP_KEY_GUID;
static const GUID guidRestore = BACKUPKEY_RESTORE_GUID;
static const GUID guidRestoreW2K = BACKUPKEY_RESTORE_GUID_W2K;
static const GUID guidBackup = BACKUPKEY_BACKUP_GUID;
DWORD
BackupRestoreData(
IN HANDLE hToken,
IN PMASTERKEY_STORED phMasterKey,
IN DWORD dwReason,
IN PBYTE pbDataIn,
IN DWORD cbDataIn,
OUT PBYTE *ppbDataOut,
OUT DWORD *pcbDataOut,
IN BOOL fBackup
)
{
return LocalBackupRestoreData(hToken,
phMasterKey,
dwReason,
pbDataIn,
cbDataIn,
ppbDataOut,
pcbDataOut,
fBackup?&guidBackup:&guidRestore);
}
DWORD
LocalBackupRestoreData(
IN HANDLE hToken,
IN PMASTERKEY_STORED phMasterKey,
IN DWORD dwReason,
IN PBYTE pbDataIn,
IN DWORD cbDataIn,
OUT PBYTE *ppbDataOut,
OUT DWORD *pcbDataOut,
IN const GUID *pguidAction
)
/*++
The caller of this function MUST be impersonating a client user.
--*/
{
WCHAR FastBuffer[ 256 ];
LPWSTR SlowBuffer = NULL;
LPWSTR pszAuditComputerName = NULL;
LPWSTR wszComputerName;
DWORD cchComputerName;
static DWORD dwLastFailTickCount; // time for failure on last access
static LUID luidLastFailAuthId; // LUID associated with failed network
DWORD dwCandidateTickCount;
LUID luidCandidateAuthId; // LUID associated with client security context.
BOOL fRediscoverDC = FALSE;
DWORD dwLastError = ERROR_NETWORK_BUSY;
BOOL fRetrieve = FALSE;
D_DebugLog((DEB_TRACE_API, "LocalBackupRestoreData\n"));
if(!FIsWinNT())
return ERROR_CALL_NOT_IMPLEMENTED;
fRetrieve = (memcmp(pguidAction, &guidRetrieve, sizeof(GUID)) == 0);
//
// impersonate the user, so we may
// 1. check the authentication ID to see if we've failed to hit the
// net as this user.
// 2. determine a domain controller computer name associated with
// the user.
// 3. backup or restore the requested material on behalf of the user.
//
if(!GetThreadAuthenticationId( GetCurrentThread(), &luidCandidateAuthId ))
return GetLastError();
//
// now, see if the network was previously unavailable (recently)
// for this user.
//
dwCandidateTickCount = GetTickCount();
if(memcmp(&luidCandidateAuthId, &luidLastFailAuthId, sizeof(LUID)) == 0) {
if( (dwLastFailTickCount + (5*1000*60)) > dwCandidateTickCount ) {
//BUGBUG: return ERROR_NETWORK_BUSY;
}
}
//
// we got far enough along that we update the failed network cache
// if something goes wrong from here.
//
network_call:
//
// get domain controller computer name associated with current
// security context.
// Try with fast static buffer first, fallback on dynamically allocated
// buffer if not large enough.
//
wszComputerName = FastBuffer;
cchComputerName = sizeof(FastBuffer) / sizeof( WCHAR );
dwLastError = CPSGetDomainControllerName(
wszComputerName,
&cchComputerName,
fRediscoverDC
);
if( dwLastError != ERROR_SUCCESS && (cchComputerName > (sizeof(FastBuffer) / sizeof(WCHAR) ))) {
SlowBuffer = (LPWSTR) SSAlloc( cchComputerName * sizeof(WCHAR) );
if( SlowBuffer ) {
wszComputerName = SlowBuffer;
dwLastError = CPSGetDomainControllerName(
wszComputerName,
&cchComputerName,
fRediscoverDC
);
}
}
if( dwLastError == ERROR_SUCCESS ) {
LPWSTR wszTargetMachine = wszComputerName;
pszAuditComputerName = wszComputerName;
//
// select action ID.
//
//
// HACKHACK workaround picky RPC/Kerberos name format behavior that
// would otherwise prevent Kerberos from being used.
//
if( wszTargetMachine[ 0 ] == L'\\' && wszTargetMachine[ 1 ] == L'\\' )
wszTargetMachine += 2;
dwLastError = BackupKey(
wszTargetMachine, // target computer.
pguidAction,
pbDataIn,
cbDataIn,
ppbDataOut,
pcbDataOut,
0
);
}
//
// Audit success or failure
//
if((memcmp(pguidAction, &guidRestore, sizeof(GUID)) == 0) ||
(memcmp(pguidAction, &guidRestoreW2K, sizeof(GUID)) == 0))
{
// Grab the recovery key id
WCHAR wszBackupkeyGuid[MAX_GUID_SZ_CHARS];
PBACKUPKEY_RECOVERY_BLOB pBackupBlob = (PBACKUPKEY_RECOVERY_BLOB)phMasterKey->pbBBK;
wszBackupkeyGuid[0] = 0;
if((pBackupBlob) && (phMasterKey->cbBBK > sizeof(BACKUPKEY_RECOVERY_BLOB)))
{
MyGuidToStringW(&pBackupBlob->guidKey, wszBackupkeyGuid);
}
CPSAudit(hToken,
SE_AUDITID_DPAPI_RECOVERY,
phMasterKey->wszguidMasterKey, // Key Identifier
pszAuditComputerName, // Recovery Server
0, // Recovery Reason
wszBackupkeyGuid, // Recovery Key ID
dwLastError); // Failure Reason
}
else if(memcmp(pguidAction, &guidBackup, sizeof(GUID)) == 0)
{
// Attempting a remote backup
// Grab the recovery key id
WCHAR wszBackupkeyGuid[MAX_GUID_SZ_CHARS];
PBACKUPKEY_RECOVERY_BLOB pBackupBlob = (PBACKUPKEY_RECOVERY_BLOB)*ppbDataOut;
wszBackupkeyGuid[0] = 0;
if(( dwLastError == ERROR_SUCCESS ) &&
(pBackupBlob) &&
(*pcbDataOut > sizeof(BACKUPKEY_RECOVERY_BLOB)))
{
MyGuidToStringW(&pBackupBlob->guidKey, wszBackupkeyGuid);
}
CPSAudit(hToken,
SE_AUDITID_DPAPI_BACKUP,
phMasterKey->wszguidMasterKey, // Key Identifier
pszAuditComputerName, // Recovery Server
0,
wszBackupkeyGuid, // Recovery Key ID
dwLastError); // Failure Reason
}
if( SlowBuffer ) {
SSFree( SlowBuffer );
SlowBuffer = NULL;
}
//
// common failure path is ERROR_ACCESS_DENIED for delegation scenarios
// where target machine isn't trusted for delegation.
// don't bother retry for this case.
//
if( dwLastError != ERROR_SUCCESS && dwLastError != ERROR_ACCESS_DENIED ) {
//
// if it failed, try once again and force DC re-discovery.
//
if( !fRediscoverDC ) {
fRediscoverDC = TRUE;
goto network_call;
}
//
// one of the network operations failed, so update the
// last failure variables so that we don't bang the network
// over-and-over.
//
dwLastFailTickCount = dwCandidateTickCount;
CopyMemory( &luidLastFailAuthId, &luidCandidateAuthId, sizeof(LUID));
}
D_DebugLog((DEB_TRACE_API, "LocalBackupRestoreData returned 0x%x\n", dwLastError));
return dwLastError;
}
BOOL
GetDomainControllerNameByToken(
IN HANDLE hToken,
IN OUT LPWSTR wszDomainControllerName,
IN OUT PDWORD pcchDomainControllerName,
IN BOOL fRediscover
)
/*++
This routine obtains a domain controller computer name associated with
the account related to the hToken access token.
hToken should be opened for TOKEN_QUERY access.
wszDomainControllerName should be of size (UNCLEN+1)
--*/
{
PSID pSidUser = NULL; // sid of client user.
WCHAR szUserName[ UNLEN + 1 ];
DWORD cchUserName = sizeof(szUserName) / sizeof(WCHAR);
WCHAR szDomainName[ DNLEN + 1]; // domain we want a controller for.
DWORD cchDomainName = sizeof(szDomainName) / sizeof(WCHAR);
SID_NAME_USE snu;
PDOMAIN_CONTROLLER_INFOW pDomainInfo = NULL;
LPWSTR wszQueryResult = NULL;
NET_API_STATUS nas;
DWORD dwGetDcFlags = 0;
BOOL fSuccess = FALSE;
if(wszDomainControllerName == NULL || pcchDomainControllerName == NULL)
return FALSE;
//
// first, get the sid of the user associated with the specified access
// token.
//
if(!GetTokenUserSid(hToken, &pSidUser))
return FALSE;
//
// next, lookup the domain name associated with the specified account.
//
if(!LookupAccountSidW(
NULL,
pSidUser,
szUserName,
&cchUserName,
szDomainName,
&cchDomainName,
&snu
)) {
SSFree(pSidUser);
return FALSE;
}
if( fRediscover )
dwGetDcFlags |= DS_FORCE_REDISCOVERY;
nas = DsGetDcNameW(
NULL,
szDomainName,
NULL,
NULL,
DS_DIRECTORY_SERVICE_REQUIRED | // make sure backend is NT5
DS_IS_FLAT_NAME |
dwGetDcFlags,
&pDomainInfo
);
if( nas == ERROR_SUCCESS )
wszQueryResult = pDomainInfo->DomainControllerName;
//
// if we made a successful query, copy it for the caller and indicate
// success if appropriate.
//
if(wszQueryResult) {
DWORD cchQueryResult = lstrlenW( wszQueryResult ) + 1;
if( *pcchDomainControllerName >= cchQueryResult ) {
CopyMemory(wszDomainControllerName, wszQueryResult, cchQueryResult * sizeof(WCHAR));
fSuccess = TRUE;
}
*pcchDomainControllerName = cchQueryResult;
}
if(pDomainInfo)
NetApiBufferFree(pDomainInfo);
if(pSidUser)
SSFree(pSidUser);
return fSuccess;
}
DWORD
WINAPI
CPSGetDomainControllerName(
IN OUT LPWSTR wszDomainControllerName,
IN OUT DWORD *pcchDomainControllerName,
IN BOOL fRediscover
)
/*++
This routine collects a domain controller computer name associated
with the current impersonated user (if one is being impersonated), or
the user associated with the pvContext outstanding client call if the
thread is not already impersonating a client.
--*/
{
HANDLE hToken = NULL;
DWORD dwLastError;
if(!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken)) {
return GetLastError();
}
if(!GetDomainControllerNameByToken(
hToken,
wszDomainControllerName,
pcchDomainControllerName,
fRediscover
)) {
dwLastError = ERROR_BAD_NET_RESP;
goto cleanup;
}
dwLastError = ERROR_SUCCESS;
cleanup:
if(hToken)
CloseHandle(hToken);
return dwLastError;
}
#define BACKUP_KEY_PREFIX L"BK-"
#define BACKUP_KEY_PREFIX_LEN 3
#define BACKUP_PUBLIC_VERSION 1
typedef struct _BACKUP_PUBLIC_KEY
{
DWORD dwVersion;
DWORD cbPublic;
DWORD cbSignature;
} BACKUP_PUBLIC_KEY, *PBACKUP_PUBLIC_KEY;
DWORD
RetrieveBackupPublicKeyFromStorage(
HANDLE hToken,
PSID pSidUser,
LPWSTR wszFilePath,
OUT PBYTE *ppbDataOut,
OUT DWORD *pcbDataOut
)
{
DWORD dwLastError = ERROR_SUCCESS;
WCHAR szUserName[ UNLEN + 1 ];
DWORD cchUserName = sizeof(szUserName) / sizeof(WCHAR);
WCHAR szDomainName[ BACKUP_KEY_PREFIX_LEN + DNLEN +1]; // domain we want a controller for.
DWORD cchDomainName = sizeof(szDomainName) / sizeof(WCHAR);
HANDLE hFile = NULL;
HANDLE hMap = NULL;
PBACKUP_PUBLIC_KEY pBackupPublic = NULL;
DWORD dwFileSizeLow;
SID_NAME_USE snu;
wcscpy(szDomainName, BACKUP_KEY_PREFIX);
//
// next, lookup the domain name associated with the specified account.
//
cchDomainName -= BACKUP_KEY_PREFIX_LEN;
if(!LookupAccountSidW(
NULL,
pSidUser,
szUserName,
&cchUserName,
szDomainName + BACKUP_KEY_PREFIX_LEN,
&cchDomainName,
&snu
)) {
return GetLastError();
}
if(hToken)
{
if(!SetThreadToken(NULL, hToken))
{
return GetLastError();
}
}
//
// Attempt to find a public key for the
// specified domain
//
cchDomainName += BACKUP_KEY_PREFIX_LEN;
//
// setup the UNICODE_STRINGs for the call.
//
dwLastError = OpenFileInStorageArea(
NULL,
GENERIC_READ,
wszFilePath,
szDomainName,
&hFile
);
if(ERROR_SUCCESS != dwLastError)
{
goto error;
}
dwFileSizeLow = GetFileSize( hFile, NULL );
if(dwFileSizeLow == 0xFFFFFFFF)
{
dwLastError = ERROR_INVALID_DATA;
goto error;
}
hMap = CreateFileMappingU(
hFile,
NULL,
PAGE_READONLY,
0,
0,
NULL
);
if(NULL == hMap)
{
dwLastError = GetLastError();
goto error;
}
pBackupPublic = (PBACKUP_PUBLIC_KEY)MapViewOfFile( hMap, FILE_MAP_READ, 0, 0, 0 );
if(NULL == pBackupPublic)
{
dwLastError = GetLastError();
goto error;
}
if((pBackupPublic->dwVersion != BACKUP_PUBLIC_VERSION) ||
(dwFileSizeLow < sizeof(BACKUP_PUBLIC_KEY) + pBackupPublic->cbPublic + pBackupPublic->cbSignature))
{
dwLastError = ERROR_INVALID_DATA;
goto error;
}
//
// Verify the signature
//
dwLastError = LogonCredVerifySignature( NULL,
(PBYTE)(pBackupPublic + 1) + pBackupPublic->cbSignature,
pBackupPublic->cbPublic,
NULL,
(PBYTE)(pBackupPublic + 1),
pBackupPublic->cbSignature);
if(ERROR_SUCCESS != dwLastError)
{
goto error;
}
*ppbDataOut = (PBYTE)SSAlloc(pBackupPublic->cbPublic);
if(NULL == ppbDataOut)
{
dwLastError = STATUS_OBJECT_NAME_NOT_FOUND;
goto error;
}
CopyMemory(*ppbDataOut, (PBYTE)(pBackupPublic+1), pBackupPublic->cbPublic);
*pcbDataOut = pBackupPublic->cbPublic;
error:
if(pBackupPublic)
{
UnmapViewOfFile(pBackupPublic);
}
if(hMap)
{
CloseHandle(hMap);
}
if(hFile)
{
CloseHandle(hFile);
}
if(hToken)
{
RevertToSelf();
}
return dwLastError;
}
DWORD
WriteBackupPublicKeyToStorage(
HANDLE hToken,
PSID pSidUser,
LPWSTR wszFilePath,
PBYTE pbData,
DWORD cbData
)
{
DWORD dwLastError = ERROR_SUCCESS;
WCHAR szUserName[ UNLEN + 1 ];
DWORD cchUserName = sizeof(szUserName) / sizeof(WCHAR);
WCHAR szDomainName[ BACKUP_KEY_PREFIX_LEN + DNLEN +1]; // domain we want a controller for.
DWORD cchDomainName = sizeof(szDomainName) / sizeof(WCHAR);
HANDLE hFile = NULL;
HANDLE hMap = NULL;
PBACKUP_PUBLIC_KEY pBackupPublic = NULL;
DWORD dwFileSizeLow;
SID_NAME_USE snu;
PBYTE pbSignature = NULL;
DWORD cbSignature;
wcscpy(szDomainName, BACKUP_KEY_PREFIX);
//
// next, lookup the domain name associated with the specified account.
//
cchDomainName -= BACKUP_KEY_PREFIX_LEN;
if(!LookupAccountSidW(
NULL,
pSidUser,
szUserName,
&cchUserName,
szDomainName + BACKUP_KEY_PREFIX_LEN,
&cchDomainName,
&snu
)) {
return GetLastError();
}
if(hToken)
{
if(!SetThreadToken(NULL, hToken))
{
return GetLastError();
}
}
//
// Attempt to find a public key for the
// specified domain
//
cchDomainName += BACKUP_KEY_PREFIX_LEN;
dwLastError = LogonCredGenerateSignature(
hToken,
pbData,
cbData,
NULL,
&pbSignature,
&cbSignature);
if(ERROR_SUCCESS != dwLastError)
{
goto error;
}
dwFileSizeLow = sizeof(BACKUP_PUBLIC_KEY) + cbData + cbSignature;
//
// setup the UNICODE_STRINGs for the call.
//
dwLastError = OpenFileInStorageArea(
NULL,
GENERIC_READ | GENERIC_WRITE,
wszFilePath,
szDomainName,
&hFile
);
if(ERROR_SUCCESS != dwLastError)
{
goto error;
}
hMap = CreateFileMappingU(
hFile,
NULL,
PAGE_READWRITE,
0,
dwFileSizeLow,
NULL
);
if(NULL == hMap)
{
dwLastError = GetLastError();
goto error;
}
pBackupPublic = (PBACKUP_PUBLIC_KEY)MapViewOfFile( hMap, FILE_MAP_WRITE , 0, 0, dwFileSizeLow );
if(NULL == pBackupPublic)
{
dwLastError = GetLastError();
goto error;
}
pBackupPublic->dwVersion = BACKUP_PUBLIC_VERSION;
pBackupPublic->cbSignature = cbSignature;
pBackupPublic->cbPublic = cbData;
CopyMemory((PBYTE)(pBackupPublic+1), pbSignature, cbSignature);
CopyMemory((PBYTE)(pBackupPublic+1) + cbSignature, pbData, cbData);
error:
if(pBackupPublic)
{
UnmapViewOfFile(pBackupPublic);
}
if(hMap)
{
CloseHandle(hMap);
}
if(hFile)
{
CloseHandle(hFile);
}
if(pbSignature)
{
SSFree(pbSignature);
}
if(hToken)
{
RevertToSelf();
}
return dwLastError;
}
DWORD
AttemptLocalBackup(
IN BOOL fRetrieve,
IN HANDLE hToken,
IN PMASTERKEY_STORED phMasterKey,
IN DWORD dwReason,
PBYTE pbMasterKey,
DWORD cbMasterKey,
PBYTE pbLocalKey,
DWORD cbLocalKey,
PBYTE *ppbBBK,
DWORD *pcbBBK
)
{
DWORD dwLastError = ERROR_SUCCESS;
PCCERT_CONTEXT pPublic = NULL;
PBYTE pbPublic = NULL;
DWORD cbPublic = 0;
HCRYPTPROV hProv = NULL;
HCRYPTKEY hPublicKey = NULL;
PBYTE pbPayloadKey = NULL;;
PBACKUPKEY_KEY_BLOB pKeyBlob = NULL;
DWORD cbKeyBlobData = 0;
DWORD cbKeyBlob = 0;
PBACKUPKEY_INNER_BLOB pInnerBlob = NULL;
DWORD cbInnerBlob = 0;
DWORD cbInnerBlobData = 0;
PBYTE pbData = NULL;
DWORD cbTemp = 0;
PBACKUPKEY_RECOVERY_BLOB pOuterBlob = NULL;
DWORD cbOuterBlob = 0;
PSID pSidUser = NULL; // sid of client user.
DWORD cbSid = 0;
WCHAR wszBackupKeyID[MAX_GUID_SZ_CHARS];
BYTE rgbThumbprint[A_SHA_DIGEST_LEN];
DWORD cbThumbprint;
wszBackupKeyID[0] = 0;
if(!GetTokenUserSid(hToken, &pSidUser)){
dwLastError = GetLastError();
goto error;
}
if(fRetrieve)
{
// Attempt to retrieve the public from
// the DC.
//
// We impersonate when we do this
//
SetThreadToken(NULL, hToken);
dwLastError = LocalBackupRestoreData(hToken,
phMasterKey,
dwReason,
pbMasterKey,
0,
&pbPublic,
&cbPublic,
&guidRetrieve);
//
// Revert back to ourself
//
SetThreadToken(NULL, NULL);
}
else
{
//
// We're attempting a backup, so first see if we have a local copy of
// the public.
//
dwLastError = RetrieveBackupPublicKeyFromStorage(hToken,
pSidUser,
phMasterKey->szFilePath,
&pbPublic,
&cbPublic);
}
if(ERROR_SUCCESS == dwLastError)
{
pPublic = CertCreateCertificateContext(X509_ASN_ENCODING,
pbPublic,
cbPublic);
if(NULL == pPublic)
{
dwLastError = GetLastError();
}
}
if(dwLastError != ERROR_SUCCESS)
{
goto error;
}
if(sizeof(GUID) == pPublic->pCertInfo->SerialNumber.cbData)
{
MyGuidToStringW((GUID *)pPublic->pCertInfo->SerialNumber.pbData, wszBackupKeyID);
}
if(fRetrieve)
{
WriteBackupPublicKeyToStorage(hToken,
pSidUser,
phMasterKey->szFilePath,
pbPublic,
cbPublic);
}
if(!CryptAcquireContext(&hProv,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT))
{
dwLastError = GetLastError();
goto error;
}
if(!CryptImportPublicKeyInfoEx(hProv,
pPublic->dwCertEncodingType,
&pPublic->pCertInfo->SubjectPublicKeyInfo,
CALG_RSA_KEYX,
NULL,
NULL,
&hPublicKey))
{
dwLastError = GetLastError();
goto error;
}
cbSid = GetLengthSid(pSidUser);
cbInnerBlobData = sizeof(BACKUPKEY_INNER_BLOB) +
cbLocalKey +
cbSid +
A_SHA_DIGEST_LEN;
//
// Round up to blocklen
//
cbInnerBlob = (cbInnerBlobData + (DES_BLOCKLEN - 1)) & ~(DES_BLOCKLEN-1);
cbTemp = sizeof(cbKeyBlob);
if(!CryptGetKeyParam(hPublicKey,
KP_BLOCKLEN,
(PBYTE)&cbKeyBlob,
&cbTemp,
0))
{
dwLastError = GetLastError();
goto error;
}
cbKeyBlob >>= 3; // convert from bits to bytes
cbOuterBlob = sizeof(BACKUPKEY_RECOVERY_BLOB) +
cbKeyBlob +
cbInnerBlob;
pOuterBlob = (PBACKUPKEY_RECOVERY_BLOB)SSAlloc(cbOuterBlob);
if(NULL == pOuterBlob)
{
dwLastError = ERROR_NOT_ENOUGH_MEMORY;
goto error;
}
pKeyBlob = (PBACKUPKEY_KEY_BLOB)(pOuterBlob+1);
pInnerBlob = (PBACKUPKEY_INNER_BLOB)((PBYTE)pKeyBlob + cbKeyBlob);
// Initialize the payload key
cbKeyBlobData = sizeof(BACKUPKEY_KEY_BLOB) + cbMasterKey + DES3_KEYSIZE + DES_BLOCKLEN;
pKeyBlob->cbMasterKey = cbMasterKey;
pKeyBlob->cbPayloadKey = DES3_KEYSIZE + DES_BLOCKLEN;
pbPayloadKey = (PBYTE)(pKeyBlob+1) + cbMasterKey;
CopyMemory((PBYTE)(pKeyBlob+1), pbMasterKey, cbMasterKey);
//
// Generate a payload key
//
RtlGenRandom(pbPayloadKey, pKeyBlob->cbPayloadKey);
// Populate the payload
pInnerBlob->dwPayloadVersion = BACKUPKEY_PAYLOAD_VERSION;
pInnerBlob->cbLocalKey = cbLocalKey;
pbData = (PBYTE)(pInnerBlob+1);
CopyMemory(pbData, pbLocalKey, cbLocalKey);
pbData += cbLocalKey;
CopyMemory(pbData, pSidUser, cbSid);
pbData += cbSid;
// Pad
if(cbInnerBlob > cbInnerBlobData)
{
RtlGenRandom(pbData, cbInnerBlob - cbInnerBlobData);
pbData += cbInnerBlob - cbInnerBlobData;
}
// Generate the payload MAC
FMyPrimitiveSHA( (PBYTE)pInnerBlob,
cbInnerBlob - A_SHA_DIGEST_LEN,
pbData);
//
// Encrypt with 3DES CBC
//
{
DES3TABLE s3DESKey;
BYTE InputBlock[DES_BLOCKLEN];
DWORD iBlock;
DWORD cBlocks = cbInnerBlob/DES_BLOCKLEN;
BYTE feedback[ DES_BLOCKLEN ];
// initialize 3des key
//
if(cBlocks*DES_BLOCKLEN != cbInnerBlob)
{
// Master key must be a multiple of DES_BLOCKLEN
dwLastError = NTE_BAD_KEY;
goto error;
}
tripledes3key(&s3DESKey, pbPayloadKey);
//
// IV is derived from the DES_BLOCKLEN bytes of the calculated
// rgbSymKey, after the 3des key
CopyMemory(feedback, pbPayloadKey + DES3_KEYSIZE, DES_BLOCKLEN);
for(iBlock=0; iBlock < cBlocks; iBlock++)
{
CopyMemory(InputBlock,
((PBYTE)pInnerBlob)+iBlock*DES_BLOCKLEN,
DES_BLOCKLEN);
CBC(tripledes,
DES_BLOCKLEN,
((PBYTE)pInnerBlob)+iBlock*DES_BLOCKLEN,
InputBlock,
&s3DESKey,
ENCRYPT,
feedback);
}
}
//
// Encrypt master key and payload key to
// the public key
if(!CryptEncrypt(hPublicKey,
NULL,
TRUE,
0, // CRYPT_OAEP
(PBYTE)pKeyBlob,
&cbKeyBlobData,
cbKeyBlob))
{
dwLastError = GetLastError();
goto error;
}
if(cbKeyBlobData != cbKeyBlob)
{
CopyMemory((PBYTE)pKeyBlob + cbKeyBlobData,
pInnerBlob,
cbInnerBlob);
cbOuterBlob -= cbKeyBlob - cbKeyBlobData;
}
pOuterBlob->dwVersion = BACKUPKEY_RECOVERY_BLOB_VERSION;
pOuterBlob->cbEncryptedMasterKey = cbKeyBlobData;
pOuterBlob->cbEncryptedPayload = cbInnerBlob;
CopyMemory(&pOuterBlob->guidKey,
pPublic->pCertInfo->SubjectUniqueId.pbData,
sizeof(GUID));
*ppbBBK = (PBYTE)pOuterBlob;
*pcbBBK = cbOuterBlob;
pOuterBlob = NULL;
error:
if((fRetrieve) || (ERROR_SUCCESS == dwLastError))
{
// Only audit if we're attempting to do the long backup.
SetThreadToken(NULL, hToken);
CPSAudit(hToken,
SE_AUDITID_DPAPI_BACKUP,
phMasterKey->wszguidMasterKey, // Key Identifier
L"", // Recovery Server
dwReason,
wszBackupKeyID, // Recovery Key ID
dwLastError); // Failure Reason
SetThreadToken(NULL, NULL);
}
if(pPublic)
{
CertFreeCertificateContext(pPublic);
}
if(pbPublic)
{
SSFree(pbPublic);
}
if(pOuterBlob)
{
SSFree(pOuterBlob);
}
if(hPublicKey)
{
CryptDestroyKey(hPublicKey);
}
if(hProv)
{
CryptReleaseContext(hProv, 0);
}
return dwLastError;
}
DWORD
WINAPI
BackupKey(
IN LPCWSTR szComputerName,
IN const GUID *pguidActionAgent,
IN BYTE *pDataIn,
IN DWORD cbDataIn,
IN OUT BYTE **ppDataOut,
IN OUT DWORD *pcbDataOut,
IN DWORD dwParam
)
{
RPC_BINDING_HANDLE h = NULL;
WCHAR *pStringBinding = NULL;
BOOL fLocal = FALSE;
HANDLE hToken = NULL;
RPC_STATUS RpcStatus = RPC_S_OK;
DWORD dwRetVal = ERROR_INVALID_PARAMETER;
DWORD i;
WCHAR szLocalComputerName[MAX_COMPUTERNAME_LENGTH + 2];
DWORD BufSize = MAX_COMPUTERNAME_LENGTH + 2;
if (!(GetComputerNameW(szLocalComputerName, &BufSize)))
{
return GetLastError();
}
*ppDataOut = NULL;
*pcbDataOut = 0;
if (!FIsWinNT5())
return ERROR_CALL_NOT_IMPLEMENTED;
//
// If we're going off machine, check to make sure that delegation is
// allowed.
//
if(!IsLocal() && (szComputerName) && (0 != wcscmp(szComputerName, szLocalComputerName)))
{
LPUSER_INFO_1 pUserInfo = NULL;
wcscat(szLocalComputerName, L"$");
if((OpenThreadToken(GetCurrentThread(),
TOKEN_IMPERSONATE,
TRUE,
&hToken)) &&
SetThreadToken(NULL, NULL))
{
if (NERR_Success == NetUserGetInfo(
szComputerName,
szLocalComputerName,
1,
(PBYTE *)&pUserInfo
)) {
if (!(UF_TRUSTED_FOR_DELEGATION & pUserInfo->usri1_flags))
{
RpcStatus = SEC_E_DELEGATION_REQUIRED;
}
NetApiBufferFree(pUserInfo);
}
// Impersonate again
if(!SetThreadToken(NULL, hToken))
{
RpcStatus = GetLastError();
}
}
if(RPC_S_OK != RpcStatus)
{
goto error;
}
}
else
{
fLocal = TRUE;
}
//
// Try all of the bindings
//
for (i = fLocal?0:1; i < g_cwzrBackupBindingList; i++)
{
RPC_SECURITY_QOS RpcQos;
if (RPC_S_OK != RpcNetworkIsProtseqValidW(
(unsigned short *)g_awzrBackupBindingList[i].pszProtSeq))
{
continue;
}
RpcStatus = RpcStringBindingComposeW(
NULL,
(unsigned short *)g_awzrBackupBindingList[i].pszProtSeq,
(unsigned short *)szComputerName,
(unsigned short *)g_awzrBackupBindingList[i].pszEndpoint,
NULL,
&pStringBinding);
if (RPC_S_OK != RpcStatus)
{
continue;
}
RpcStatus = RpcBindingFromStringBindingW(
pStringBinding,
&h);
if (NULL != pStringBinding)
{
RpcStringFreeW(&pStringBinding);
}
if (RPC_S_OK != RpcStatus)
{
continue;
}
RpcStatus = RpcEpResolveBinding(
h,
BackupKey_v1_0_c_ifspec);
if (RPC_S_OK != RpcStatus)
{
continue;
}
//
// enable privacy and negotiated re-authentication.
// a fresh authentication is required in the event an existing connection
// to the target machine already existed which was made with non-default
// credentials.
//
ZeroMemory( &RpcQos, sizeof(RpcQos) );
RpcQos.Version = RPC_C_SECURITY_QOS_VERSION;
RpcQos.Capabilities = RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH;
RpcQos.IdentityTracking = RPC_C_QOS_IDENTITY_DYNAMIC;
RpcQos.ImpersonationType = RPC_C_IMP_LEVEL_IMPERSONATE;
RpcStatus = RpcBindingSetAuthInfoExW(
h,
(LPWSTR)szComputerName,
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
RPC_C_AUTHN_GSS_NEGOTIATE,
0,
0,
&RpcQos
);
if (RPC_S_OK != RpcStatus)
{
continue;
}
__try
{
dwRetVal = BackuprKey(
h,
(GUID*)pguidActionAgent,
pDataIn,
cbDataIn,
ppDataOut,
pcbDataOut,
dwParam
);
}
__except ( EXCEPTION_EXECUTE_HANDLER )
{
RpcStatus = _exception_code();
}
if (RPC_S_OK == RpcStatus)
{
break;
}
}
error:
if(hToken)
{
CloseHandle(hToken);
}
if(RPC_S_OK != RpcStatus)
{
dwRetVal = RpcStatus;
}
if(h)
{
RpcBindingFree(&h);
}
return dwRetVal;
}