mirror of https://github.com/tongzx/nt5src
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.
6989 lines
172 KiB
6989 lines
172 KiB
/*++
|
|
|
|
Copyright (c) 1997-1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
efssrv.cxx
|
|
|
|
Abstract:
|
|
|
|
EFS (Encrypting File System) Server
|
|
|
|
Author:
|
|
|
|
Robert Reichel (RobertRe)
|
|
Robert Gu (RobertG)
|
|
|
|
Environment:
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include <lsapch.hxx>
|
|
|
|
extern "C" {
|
|
#include <nt.h>
|
|
#include <ntdef.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <efsstruc.h>
|
|
#include <lmaccess.h>
|
|
#include <lmcons.h>
|
|
#include <lmapibuf.h>
|
|
#include <userenv.h>
|
|
#include <userenvp.h>
|
|
#include "lsasrvp.h"
|
|
#include "debug.h"
|
|
#include "efssrv.hxx"
|
|
#include "userkey.h"
|
|
}
|
|
|
|
#define ALGORITHM_ID TEXT("AlgorithmID")
|
|
#define KEYCACHEPERIOD TEXT("KeyCacheValidationPeriod")
|
|
#define FIPSPOLICY TEXT("FipsAlgorithmPolicy")
|
|
#define EFSCONFIG TEXT("EfsConfiguration")
|
|
#define EFSLASTGOODCONFIG TEXT("LastGoodEfsConfiguration")
|
|
|
|
#define TRUSTEDPEOPLE TEXT("TrustedPeople")
|
|
|
|
//
|
|
// The following key GPOSTATUSKEY is a temp solution. GPO should provide an API to tell people
|
|
// if the GP propagation succeeded or not.
|
|
//
|
|
|
|
#define GPOSTATUSKEY TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\GPExtensions\\{35378EAC-683F-11D2-A89A-00C04FBBCFA2}")
|
|
#define GPSTATUS TEXT("Status")
|
|
|
|
//
|
|
// EFS key
|
|
//
|
|
|
|
#define EFSMACHINEKEY TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\EFS")
|
|
#define EFSPOLKEY TEXT("SOFTWARE\\Policies\\Microsoft\\Windows NT\\CurrentVersion\\EFS")
|
|
|
|
#define POLICYUSEFIPS 1
|
|
#define DISABLEEFS 0x00000001
|
|
|
|
// Default cache length is 3600 seconds
|
|
|
|
#define MAXCACHELENGTH 86400 * 7 // Max cache period - 7 Days. We only check time valid.
|
|
#define MINCACHELENGTH 1800 // Min cache period - 0.5 Hours
|
|
|
|
#define TIME_UNIT 10000000 // 1 TIME_UNIT == 1 second
|
|
|
|
extern LONG RecoveryCertIsValidating;
|
|
extern HANDLE EfsWaitHandle;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// /
|
|
// Define how long our FEKs can be. An FEK will be allocated into a fixed /
|
|
// size buffer, but only a certain number of bits of entropy may be used in /
|
|
// the export version. /
|
|
// /
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// This number affects the FEK generation algorithm. It represents the number
|
|
// of bits of entropy in the key.
|
|
//
|
|
|
|
const EXPORT_KEY_STRENGTH = 56;
|
|
const DOMESTIC_KEY_STRENGTH = 128;
|
|
const EXPORT_DESX_SALT_LENGTH = 9;
|
|
const DES3_KEY_STRENGTH = 168;
|
|
const AES_KEY_STRENGTH_256 = 256;
|
|
|
|
const DWORD WAITFORCERTVALIDATE = 10000;
|
|
|
|
#ifndef LSASRV_EXPORT
|
|
|
|
const DWORD KeyEntropy = DOMESTIC_KEY_STRENGTH;
|
|
|
|
#else
|
|
|
|
const DWORD KeyEntropy = EXPORT_KEY_STRENGTH;
|
|
|
|
#endif
|
|
|
|
LONG EFSDebugLevel = 0;
|
|
|
|
|
|
ALG_ID EfsAlgInForce = CALG_AES_256;
|
|
extern "C" BOOLEAN EfsDisabled = FALSE;
|
|
|
|
//
|
|
// Current recovery policy
|
|
//
|
|
|
|
RTL_RESOURCE RecoveryPolicyResource;
|
|
|
|
CURRENT_RECOVERY_POLICY CurrentRecoveryPolicy;
|
|
|
|
DWORD MissingRecoveryPolicyLogged = 0;
|
|
|
|
//
|
|
// Functions in EFSAPI.CXX
|
|
//
|
|
|
|
BOOLEAN
|
|
EncryptFSCTLData(
|
|
IN ULONG Fsctl,
|
|
IN ULONG Psc,
|
|
IN ULONG Csc,
|
|
IN PVOID EfsData,
|
|
IN ULONG EfsDataLength,
|
|
IN OUT PUCHAR Buffer,
|
|
IN OUT PULONG BufferLength
|
|
);
|
|
|
|
BOOLEAN
|
|
SendHandle(
|
|
IN HANDLE Handle,
|
|
IN OUT PUCHAR EfsData,
|
|
IN OUT PULONG EfsDataLength
|
|
);
|
|
|
|
BOOLEAN
|
|
SendEfs(
|
|
IN PEFS_KEY Fek,
|
|
IN PEFS_DATA_STREAM_HEADER Efs,
|
|
OUT PUCHAR EfsData,
|
|
OUT PULONG EfsDataLength
|
|
);
|
|
|
|
BOOLEAN
|
|
SendHandleAndEfs(
|
|
IN HANDLE Handle,
|
|
IN PEFS_DATA_STREAM_HEADER Efs,
|
|
IN OUT PUCHAR EfsData,
|
|
IN OUT PULONG EfsDataLength
|
|
);
|
|
|
|
//
|
|
// Function prototypes in this module
|
|
//
|
|
|
|
BOOLEAN
|
|
CreatePublicKeyInformationThumbprint(
|
|
IN PSID pUserSid,
|
|
IN PBYTE pbCertHash,
|
|
IN DWORD cbCertHash,
|
|
IN LPWSTR lpDisplayInformation OPTIONAL,
|
|
IN LPWSTR ContainerName OPTIONAL,
|
|
IN LPWSTR ProviderName OPTIONAL,
|
|
OUT PEFS_PUBLIC_KEY_INFO * PublicKeyInformation
|
|
);
|
|
|
|
PBYTE
|
|
EncryptFEK(
|
|
IN PEFS_KEY Fek,
|
|
IN HCRYPTKEY hRSAKey,
|
|
OUT PDWORD dwEncryptedFEKLength
|
|
);
|
|
|
|
PEFS_KEY
|
|
ExtractFek(
|
|
IN PEFS_USER_INFO pEfsUserInfo,
|
|
IN PENCRYPTED_KEY EncryptedKey,
|
|
IN BOOL CheckBits
|
|
);
|
|
|
|
DWORD
|
|
ConstructEncryptedKey(
|
|
IN PBYTE EncryptedFEK,
|
|
IN DWORD dwEncryptedFEKLength,
|
|
IN PEFS_PUBLIC_KEY_INFO PublicKeyInformation,
|
|
IN PEFS_KEY_SALT pEfsKeySalt,
|
|
OUT PENCRYPTED_KEY *EncryptedKey,
|
|
IN OUT PDWORD EncryptedKeySize
|
|
);
|
|
|
|
DWORD
|
|
ConstructKeyRing(
|
|
IN PEFS_KEY Fek,
|
|
IN DWORD KeyCount,
|
|
IN LPWSTR KeyNames[] OPTIONAL,
|
|
IN LPWSTR ProviderNames[] OPTIONAL,
|
|
IN PBYTE PublicKeys[],
|
|
IN DWORD PublicKeyLengths[],
|
|
IN PBYTE pbHashes[],
|
|
IN DWORD cbHashes[],
|
|
IN LPWSTR lpDisplayInformation[],
|
|
IN PSID pSid[],
|
|
IN BOOLEAN PublicKeyHandle,
|
|
OUT PENCRYPTED_KEYS *KeyRing,
|
|
OUT PDWORD KeyRingLength
|
|
);
|
|
|
|
DWORD
|
|
ReformatPolicyInformation(
|
|
PLSAPR_POLICY_DOMAIN_EFS_INFO PolicyEfsInfo,
|
|
PLSAPR_POLICY_DOMAIN_EFS_INFO * NewPolicyEfsInfo,
|
|
PBOOLEAN Reformatted
|
|
);
|
|
|
|
DWORD
|
|
InitRecoveryPolicy(
|
|
VOID
|
|
);
|
|
|
|
|
|
DWORD
|
|
ParseOldRecoveryData(
|
|
IN PLSAPR_POLICY_DOMAIN_EFS_INFO PolicyEfsInfo OPTIONAL,
|
|
OUT PCURRENT_RECOVERY_POLICY ParsedRecoveryPolicy
|
|
);
|
|
|
|
VOID
|
|
DumpPublicKeyInfo(
|
|
PEFS_PUBLIC_KEY_INFO PublicKeyInfo
|
|
);
|
|
|
|
void
|
|
DumpRecoveryKey(
|
|
PRECOVERY_KEY_1_1 pRecoveryKey
|
|
);
|
|
|
|
PEFS_DATA_STREAM_HEADER
|
|
AssembleEfsStream(
|
|
IN PENCRYPTED_KEYS pDDF,
|
|
IN DWORD cbDDF,
|
|
IN PENCRYPTED_KEYS pDRF,
|
|
IN DWORD cbDRF,
|
|
IN PEFS_KEY Fek
|
|
);
|
|
|
|
PENCRYPTED_KEY
|
|
GetEncryptedKeyByIndex(
|
|
PENCRYPTED_KEYS pEncryptedKeys,
|
|
DWORD KeyIndex
|
|
);
|
|
|
|
BOOL
|
|
DeleteEncryptedKeyByIndex(
|
|
IN PEFS_DATA_STREAM_HEADER pEfs,
|
|
IN DWORD KeyIndex,
|
|
IN PEFS_KEY Fek,
|
|
OUT PEFS_DATA_STREAM_HEADER * pNewEfs
|
|
);
|
|
|
|
BOOLEAN
|
|
EqualEncryptedKeys(
|
|
IN PENCRYPTED_KEYS SrcKeys,
|
|
IN PENCRYPTED_KEYS DstKeys,
|
|
IN DWORD cbDstKeys
|
|
);
|
|
|
|
//
|
|
// Server
|
|
//
|
|
|
|
VOID
|
|
EfsGetRegSettings(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called during server initialization to set
|
|
the EFS encryption algorithm.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
LONG rc;
|
|
HKEY EfsKey;
|
|
DWORD AlgId;
|
|
DWORD CacheLength;
|
|
DWORD EfsConfig;
|
|
DWORD SizeInfo;
|
|
DWORD Type;
|
|
|
|
|
|
rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
TEXT("SYSTEM\\CurrentControlSet\\Control\\LSA"),
|
|
0,
|
|
GENERIC_READ,
|
|
&EfsKey
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
SizeInfo = sizeof(DWORD);
|
|
rc = RegQueryValueEx(
|
|
EfsKey,
|
|
FIPSPOLICY,
|
|
NULL,
|
|
&Type,
|
|
(PUCHAR) &AlgId,
|
|
&SizeInfo
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
if ( AlgId== POLICYUSEFIPS ) {
|
|
EfsAlgInForce = CALG_3DES;
|
|
}
|
|
|
|
}
|
|
RegCloseKey( EfsKey );
|
|
}
|
|
|
|
rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
EFSMACHINEKEY,
|
|
0,
|
|
GENERIC_READ,
|
|
&EfsKey
|
|
);
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
if (EfsAlgInForce == CALG_AES_256) {
|
|
|
|
//
|
|
// FIPS Policy does not say we have to use FIPS. Let's check if user says EFS
|
|
// should use specific algorithm.
|
|
//
|
|
|
|
SizeInfo = sizeof(DWORD);
|
|
rc = RegQueryValueEx(
|
|
EfsKey,
|
|
ALGORITHM_ID,
|
|
NULL,
|
|
&Type,
|
|
(PUCHAR) &AlgId,
|
|
&SizeInfo
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
switch (AlgId) {
|
|
|
|
case CALG_3DES:
|
|
|
|
EfsAlgInForce = CALG_3DES; //0x6603
|
|
break;
|
|
|
|
case CALG_DESX:
|
|
EfsAlgInForce = CALG_DESX; //0x6604
|
|
break;
|
|
|
|
case CALG_AES_256:
|
|
|
|
//
|
|
// Fall through intended
|
|
//
|
|
|
|
default:
|
|
// EfsAlgInForce = CALG_AES_256;//0x6610
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
SizeInfo = sizeof(DWORD);
|
|
rc = RegQueryValueEx(
|
|
EfsKey,
|
|
KEYCACHEPERIOD,
|
|
NULL,
|
|
&Type,
|
|
(PUCHAR) &CacheLength,
|
|
&SizeInfo
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
if ((CacheLength >= MINCACHELENGTH) && (CacheLength <= MAXCACHELENGTH)){
|
|
CACHE_CERT_VALID_TIME = CacheLength * TIME_UNIT;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check if EFS is disabled in Policy
|
|
//
|
|
|
|
SizeInfo = sizeof(DWORD);
|
|
rc = RegQueryValueEx(
|
|
EfsKey,
|
|
EFSCONFIG,
|
|
NULL,
|
|
&Type,
|
|
(PUCHAR) &EfsConfig,
|
|
&SizeInfo
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
if (EfsConfig & DISABLEEFS){
|
|
EfsDisabled = TRUE;
|
|
}
|
|
}
|
|
RegCloseKey( EfsKey );
|
|
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
EfsIsGpoGood(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is a temp workaround to check if GP propagation succeeded or not.
|
|
|
|
GP should provide an API to do this.
|
|
|
|
Arguments:
|
|
|
|
Not used.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
LONG rc;
|
|
HKEY PolKey;
|
|
DWORD SizeInfo;
|
|
DWORD PolStatus = 0;
|
|
DWORD Type;
|
|
BOOL GoodPol = TRUE;
|
|
|
|
rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
GPOSTATUSKEY,
|
|
0,
|
|
GENERIC_READ,
|
|
&PolKey
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Check if EFS is disabled in Policy
|
|
//
|
|
|
|
SizeInfo = sizeof(DWORD);
|
|
rc = RegQueryValueEx(
|
|
PolKey,
|
|
GPSTATUS,
|
|
NULL,
|
|
&Type,
|
|
(PUCHAR) &PolStatus,
|
|
&SizeInfo
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
if (PolStatus) {
|
|
|
|
//
|
|
//Last policy propagation failed
|
|
//
|
|
|
|
GoodPol = FALSE;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Assuming last propagation failed
|
|
//
|
|
|
|
GoodPol = FALSE;
|
|
}
|
|
|
|
RegCloseKey( PolKey );
|
|
|
|
} else {
|
|
|
|
GoodPol = FALSE;
|
|
|
|
}
|
|
|
|
return GoodPol;
|
|
|
|
}
|
|
|
|
VOID
|
|
EfsRemoveKey(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine removes EFS Last Good Policy Key.
|
|
|
|
Arguments:
|
|
|
|
No.
|
|
|
|
Return Value:
|
|
|
|
No.
|
|
|
|
--*/
|
|
{
|
|
|
|
LONG rc;
|
|
HKEY EfsKey;
|
|
|
|
rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
EFSMACHINEKEY,
|
|
0,
|
|
GENERIC_READ | KEY_SET_VALUE,
|
|
&EfsKey
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Delete the last good key
|
|
//
|
|
|
|
RegDeleteValue(
|
|
EfsKey,
|
|
EFSLASTGOODCONFIG
|
|
);
|
|
|
|
RegCloseKey( EfsKey );
|
|
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
EfsApplyGoodPolicy(
|
|
IN BOOLEAN* pEfsDisabled
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is a common routine to apply good policy data.
|
|
|
|
Arguments:
|
|
|
|
pEfsDisabled -- Point to the global EfsDisabled.
|
|
|
|
Return Value:
|
|
|
|
TRUE if we applied the data. FALSE if no data available.
|
|
|
|
--*/
|
|
{
|
|
|
|
LONG rc;
|
|
HKEY EfsKey;
|
|
HKEY EfsPolKey;
|
|
DWORD EfsConfig;
|
|
DWORD SizeInfo;
|
|
DWORD Type;
|
|
BOOL PolicyValueApplied = FALSE;
|
|
|
|
//
|
|
// Open EFS policy key
|
|
//
|
|
|
|
rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
EFSPOLKEY,
|
|
0,
|
|
GENERIC_READ,
|
|
&EfsPolKey
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Let's try to get the latest value
|
|
//
|
|
|
|
|
|
SizeInfo = sizeof(DWORD);
|
|
rc = RegQueryValueEx(
|
|
EfsPolKey,
|
|
EFSCONFIG,
|
|
NULL,
|
|
&Type,
|
|
(PUCHAR) &EfsConfig,
|
|
&SizeInfo
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
PolicyValueApplied = TRUE;
|
|
|
|
if (EfsConfig & DISABLEEFS){
|
|
|
|
if (!(*pEfsDisabled)) {
|
|
*pEfsDisabled = TRUE;
|
|
}
|
|
|
|
} else {
|
|
|
|
if (*pEfsDisabled) {
|
|
*pEfsDisabled = FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// We need to update our LAST Good key.
|
|
//
|
|
|
|
DWORD Disposition = 0;
|
|
|
|
rc = RegCreateKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
EFSMACHINEKEY,
|
|
0,
|
|
TEXT("REG_SZ"),
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
NULL,
|
|
&EfsKey,
|
|
&Disposition // address of disposition value buffer
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// OK. Let's upadte the value
|
|
//
|
|
|
|
RegSetValueEx(
|
|
EfsKey,
|
|
EFSLASTGOODCONFIG,
|
|
0,
|
|
REG_DWORD,
|
|
(CONST BYTE *)&EfsConfig,
|
|
sizeof(DWORD)
|
|
);
|
|
|
|
RegCloseKey( EfsKey );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
RegCloseKey( EfsPolKey );
|
|
|
|
}
|
|
|
|
return (PolicyValueApplied);
|
|
|
|
}
|
|
|
|
VOID
|
|
EfsApplyLastPolicy(
|
|
IN BOOLEAN *pEfsDisabled
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called during boot init time.
|
|
|
|
Arguments:
|
|
|
|
pEfsDisabled -- Point to the global EfsDisabled. May be changed to a structure pointer later
|
|
to support more EFS policy vars.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
LONG rc;
|
|
HKEY EfsKey;
|
|
DWORD EfsConfig;
|
|
DWORD SizeInfo;
|
|
DWORD Type;
|
|
|
|
if (EfsIsGpoGood()) {
|
|
|
|
//
|
|
// We got a good policy.
|
|
//
|
|
|
|
BOOL PolicyValueApplied;
|
|
|
|
PolicyValueApplied = EfsApplyGoodPolicy(
|
|
pEfsDisabled
|
|
);
|
|
|
|
if (!PolicyValueApplied) {
|
|
|
|
//
|
|
// Policy key is missing or value removed. We need to delete the last good value.
|
|
// The last good value could be non-existing. It does not hurt to try again during
|
|
// the boot.
|
|
//
|
|
|
|
EfsRemoveKey();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Last Policy propagation failed. Tried to get the last good one if there is one.
|
|
//
|
|
|
|
rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
EFSMACHINEKEY,
|
|
0,
|
|
GENERIC_READ,
|
|
&EfsKey
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
SizeInfo = sizeof(DWORD);
|
|
rc = RegQueryValueEx(
|
|
EfsKey,
|
|
EFSLASTGOODCONFIG,
|
|
NULL,
|
|
&Type,
|
|
(PUCHAR) &EfsConfig,
|
|
&SizeInfo
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
if (EfsConfig & DISABLEEFS){
|
|
|
|
if (!(*pEfsDisabled)) {
|
|
*pEfsDisabled = TRUE;
|
|
}
|
|
|
|
} else {
|
|
|
|
if (*pEfsDisabled) {
|
|
*pEfsDisabled = FALSE;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
RegCloseKey( EfsKey );
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
EfsGetPolRegSettings(
|
|
IN PVOID pEfsPolCallBack,
|
|
IN BOOLEAN timeExpired
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called during policy propagation.
|
|
|
|
Arguments:
|
|
|
|
pEfsDisabled -- Point to a structure EFS_POL_CALLBACK.
|
|
|
|
timeExpired -- FALSE if trigged by the event.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
LONG rc;
|
|
HKEY EfsKey;
|
|
DWORD EfsConfig;
|
|
DWORD SizeInfo;
|
|
DWORD Type;
|
|
BOOLEAN * CrntEfsDisabled = ((PEFS_POL_CALLBACK) pEfsPolCallBack)->EfsDisable;
|
|
|
|
if (timeExpired) {
|
|
|
|
//
|
|
// May be killed.
|
|
//
|
|
|
|
if (*(((PEFS_POL_CALLBACK)pEfsPolCallBack)->EfsPolicyEventHandle)) {
|
|
UnregisterGPNotification(*(((PEFS_POL_CALLBACK) pEfsPolCallBack)->EfsPolicyEventHandle));
|
|
CloseHandle(*(((PEFS_POL_CALLBACK) pEfsPolCallBack)->EfsPolicyEventHandle));
|
|
*(((PEFS_POL_CALLBACK) pEfsPolCallBack)->EfsPolicyEventHandle) = 0;
|
|
}
|
|
if (EfsWaitHandle) {
|
|
RtlDeregisterWait(EfsWaitHandle);
|
|
EfsWaitHandle = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (EfsIsGpoGood()) {
|
|
|
|
//
|
|
// We got a good policy.
|
|
//
|
|
|
|
BOOL PolicyValueApplied;
|
|
|
|
PolicyValueApplied = EfsApplyGoodPolicy(
|
|
CrntEfsDisabled
|
|
);
|
|
|
|
if (!PolicyValueApplied) {
|
|
|
|
//
|
|
// Policy key is missing or value removed. We need to delete the last good value.
|
|
//
|
|
|
|
rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
EFSMACHINEKEY,
|
|
0,
|
|
GENERIC_READ | KEY_SET_VALUE,
|
|
&EfsKey
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Set back the default value first.
|
|
//
|
|
|
|
SizeInfo = sizeof(DWORD);
|
|
rc = RegQueryValueEx(
|
|
EfsKey,
|
|
EFSCONFIG,
|
|
NULL,
|
|
&Type,
|
|
(PUCHAR) &EfsConfig,
|
|
&SizeInfo
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
if (EfsConfig & DISABLEEFS){
|
|
|
|
if (!(*CrntEfsDisabled)) {
|
|
*CrntEfsDisabled = TRUE;
|
|
}
|
|
|
|
} else {
|
|
|
|
if (*CrntEfsDisabled) {
|
|
*CrntEfsDisabled = FALSE;
|
|
}
|
|
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// No default value is treated as enable EFS
|
|
//
|
|
|
|
if (*CrntEfsDisabled) {
|
|
*CrntEfsDisabled = FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Delete the last good key
|
|
//
|
|
|
|
RegDeleteValue(
|
|
EfsKey,
|
|
EFSLASTGOODCONFIG
|
|
);
|
|
|
|
RegCloseKey( EfsKey );
|
|
|
|
} else {
|
|
|
|
//
|
|
// No default key value. Enable EFS if not now.
|
|
//
|
|
|
|
if (*CrntEfsDisabled) {
|
|
*CrntEfsDisabled = FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
if (EfsWaitHandle) {
|
|
|
|
//
|
|
// Unregister the last one
|
|
//
|
|
|
|
RtlDeregisterWait(EfsWaitHandle);
|
|
EfsWaitHandle = 0;
|
|
|
|
//
|
|
// Reset the notification event
|
|
//
|
|
|
|
if (*(((PEFS_POL_CALLBACK) pEfsPolCallBack)->EfsPolicyEventHandle)) {
|
|
|
|
ResetEvent(*(((PEFS_POL_CALLBACK) pEfsPolCallBack)->EfsPolicyEventHandle));
|
|
|
|
|
|
//
|
|
// Reregister for a new one
|
|
//
|
|
|
|
if (!NT_SUCCESS(RtlRegisterWait(
|
|
&EfsWaitHandle,
|
|
*(((PEFS_POL_CALLBACK)pEfsPolCallBack)->EfsPolicyEventHandle),
|
|
EfsGetPolRegSettings,
|
|
pEfsPolCallBack,
|
|
INFINITE,
|
|
WT_EXECUTEONLYONCE))){
|
|
|
|
//
|
|
// We couldn't use the thread pool.
|
|
//
|
|
|
|
UnregisterGPNotification(*(((PEFS_POL_CALLBACK)pEfsPolCallBack)->EfsPolicyEventHandle));
|
|
CloseHandle(*(((PEFS_POL_CALLBACK) pEfsPolCallBack)->EfsPolicyEventHandle));
|
|
*(((PEFS_POL_CALLBACK) pEfsPolCallBack)->EfsPolicyEventHandle) = 0;
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
RecoveryInformationCallback(
|
|
POLICY_NOTIFICATION_INFORMATION_CLASS ChangedInfoClass
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Callback for when EFS Recovery policy information changes
|
|
|
|
Arguments:
|
|
|
|
ChangedInfoClass - The info class that changed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
InitRecoveryPolicy();
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
EfspRoleChangeCallback(
|
|
POLICY_NOTIFICATION_INFORMATION_CLASS ChangedInfoClass
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Callback for when the role of the machine in a domain changes.
|
|
|
|
Arguments:
|
|
|
|
ChangedInfoClass - The info class that changed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PPOLICY_PRIMARY_DOMAIN_INFO PrimaryDomainInfo;
|
|
|
|
Status = LsarQueryInformationPolicy(LsapPolicyHandle,
|
|
PolicyPrimaryDomainInformation,
|
|
(PLSAPR_POLICY_INFORMATION *)&PrimaryDomainInfo
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DebugLog((DEB_ERROR, "Failed to query primary domain from Lsa, Status = 0x%lx\n", Status));
|
|
} else {
|
|
if (PrimaryDomainInfo->Sid != NULL) {
|
|
EfspInDomain = TRUE;
|
|
} else {
|
|
EfspInDomain = FALSE;
|
|
}
|
|
|
|
LsaFreeMemory( PrimaryDomainInfo );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
#if 0
|
|
|
|
//
|
|
// We may revisit this function in inheritance work. Keep it for now.
|
|
//
|
|
|
|
BOOL
|
|
GetPublicKey(
|
|
HCRYPTKEY hKey,
|
|
PBYTE * PublicKeyBlob,
|
|
PDWORD KeyLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Exports a public key
|
|
|
|
Arguments:
|
|
|
|
hKey - Supplies the key handle to be exported
|
|
|
|
PublicKeyBlob - Returns a buffer containing the exported key
|
|
|
|
KeyLength - Returns the length of the exported key buffer in bytes.
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
*KeyLength = 0;
|
|
*PublicKeyBlob = NULL;
|
|
|
|
if (hKey == NULL) {
|
|
ASSERT( FALSE );
|
|
return( FALSE );
|
|
}
|
|
|
|
BOOL b = CryptExportKey( hKey, 0, PUBLICKEYBLOB, 0, NULL, KeyLength );
|
|
|
|
if (b) {
|
|
|
|
*PublicKeyBlob = (PBYTE) LsapAllocateLsaHeap( *KeyLength );
|
|
|
|
if (*PublicKeyBlob != NULL) {
|
|
|
|
b = CryptExportKey( hKey, 0, PUBLICKEYBLOB, 0, *PublicKeyBlob, KeyLength );
|
|
|
|
if (!b) {
|
|
|
|
LsapFreeLsaHeap( *PublicKeyBlob );
|
|
*PublicKeyBlob = NULL;
|
|
}
|
|
|
|
} else {
|
|
|
|
b = FALSE;
|
|
}
|
|
}
|
|
|
|
return( b );
|
|
}
|
|
#endif
|
|
|
|
inline
|
|
VOID
|
|
AcquireRecoveryPolicyReadLock()
|
|
{
|
|
BOOL b = RtlAcquireResourceShared( &RecoveryPolicyResource, TRUE );
|
|
|
|
ASSERT( b );
|
|
}
|
|
|
|
inline
|
|
VOID
|
|
ReleaseRecoveryPolicyReadLock()
|
|
{
|
|
RtlReleaseResource( &RecoveryPolicyResource );
|
|
}
|
|
|
|
inline
|
|
VOID
|
|
ReleaseRecoveryData()
|
|
{
|
|
ReleaseRecoveryPolicyReadLock();
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
GetRecoveryData(
|
|
OUT PDWORD dwKeyCount,
|
|
OUT PDWORD dwPolicyStatus,
|
|
OUT PBYTE * pbPublicKeys[],
|
|
OUT DWORD * cbPublicKeys[],
|
|
OUT PBYTE * pbHashes[],
|
|
OUT DWORD * cbHashes[],
|
|
OUT LPWSTR * lpDisplayInfo[] OPTIONAL,
|
|
OUT PSID * pSid[] OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns the current recovery data. It takes a read
|
|
lock on the recovery data so that it cannot be modified while in use.
|
|
This lock must be freed by calling ReleaseRecoveryData().
|
|
|
|
Arguments:
|
|
|
|
dwKeyCount - Returns the list of keys in the current recovery data.
|
|
|
|
dwPolicyStatus - The status of the recovery policy.
|
|
|
|
pbPublicKeys - Returns an array of pointers to exported public key blobs that will be used
|
|
to encrypt the FEK.
|
|
|
|
cbPublicKeys - Specifies the length (in bytes) of each of the keys returned
|
|
in the PublicKeys array.
|
|
|
|
pbHashes - Returns an array of pointers to the key hashes.
|
|
|
|
cbHashes - Specifies the length (in bytes) of each of the key hashes.
|
|
|
|
lpDisplayInfo - Recovery cert display information.
|
|
|
|
pSid - Sids of the recovery agents
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS for succeed.
|
|
|
|
--*/
|
|
|
|
{
|
|
AcquireRecoveryPolicyReadLock();
|
|
|
|
//
|
|
// Verify that all of the cert contexts are still valid.
|
|
//
|
|
// If any of them fail, say that there is no recovery
|
|
// policy on the system.
|
|
//
|
|
|
|
DWORD i;
|
|
BOOLEAN fResult = TRUE;
|
|
|
|
LARGE_INTEGER TimeStamp;
|
|
|
|
if ( (*dwPolicyStatus = CurrentRecoveryPolicy.PolicyStatus) < RECOVERY_POLICY_OK) {
|
|
|
|
*dwKeyCount = CurrentRecoveryPolicy.dwKeyCount;
|
|
*pbPublicKeys = CurrentRecoveryPolicy.pbPublicKeys;
|
|
*cbPublicKeys = CurrentRecoveryPolicy.cbPublicKeys;
|
|
*pbHashes = CurrentRecoveryPolicy.pbHash;
|
|
*cbHashes = CurrentRecoveryPolicy.cbHash;
|
|
|
|
if (lpDisplayInfo) {
|
|
*lpDisplayInfo = CurrentRecoveryPolicy.lpDisplayInfo;
|
|
}
|
|
|
|
if (pSid) {
|
|
*pSid = CurrentRecoveryPolicy.pSid;
|
|
}
|
|
|
|
return( ERROR_SUCCESS );
|
|
}
|
|
|
|
TimeStamp.QuadPart = 0;
|
|
|
|
//
|
|
// Check if we need validate the certs again
|
|
//
|
|
|
|
if ((NT_SUCCESS( NtQuerySystemTime(&TimeStamp)) &&
|
|
(TimeStamp.QuadPart - CurrentRecoveryPolicy.TimeStamp.QuadPart > CACHE_CERT_VALID_TIME )) ||
|
|
(CurrentRecoveryPolicy.CertValidated == CERT_NOT_VALIDATED)){
|
|
|
|
//
|
|
// We only let one thread in here.
|
|
//
|
|
|
|
LONG IsCertBeingValidated;
|
|
|
|
IsCertBeingValidated = InterlockedExchange(&RecoveryCertIsValidating, 1);
|
|
|
|
if ((CurrentRecoveryPolicy.CertValidated == CERT_NOT_VALIDATED) && (IsCertBeingValidated == 1)) {
|
|
|
|
//
|
|
// If the recovery cert has not been validated and some other thread is validating,
|
|
// let's wait for a 10 seconds.
|
|
//
|
|
|
|
Sleep(WAITFORCERTVALIDATE);
|
|
if (CurrentRecoveryPolicy.CertValidated == CERT_NOT_VALIDATED) {
|
|
|
|
//
|
|
// Not validated yet. Let's try to grab the lock. Let other thread to wait.
|
|
//
|
|
|
|
IsCertBeingValidated = InterlockedExchange(&RecoveryCertIsValidating, 1);
|
|
}
|
|
|
|
}
|
|
|
|
if ( (IsCertBeingValidated != 1) || (CurrentRecoveryPolicy.CertValidated == CERT_NOT_VALIDATED) ) {
|
|
|
|
//
|
|
// No thread is validating the cert, let's do it
|
|
//
|
|
|
|
for (i=0; i<CurrentRecoveryPolicy.dwKeyCount; i++) {
|
|
|
|
//
|
|
// We only check the time in the cert
|
|
//
|
|
|
|
LONG CertTimeValid;
|
|
|
|
if (CertTimeValid = CertVerifyTimeValidity(
|
|
NULL,
|
|
CurrentRecoveryPolicy.pCertContext[i]->pCertInfo
|
|
)){
|
|
if ( CertTimeValid > 0 ) {
|
|
|
|
DebugLog((DEB_WARN, "Expired certificate in recovery policy\n"));
|
|
*dwPolicyStatus = RECOVERY_POLICY_EXPIRED_CERTS;
|
|
|
|
fResult = FALSE;
|
|
break;
|
|
} else {
|
|
DebugLog((DEB_WARN, "Expired certificate in recovery policy\n"));
|
|
*dwPolicyStatus = RECOVERY_POLICY_NOT_EFFECT_CERTS;
|
|
|
|
fResult = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// When policy is propagated, the write lock is acquired. When we get here, we are having read lock and no one
|
|
// is having the write lock. It is OK for threads stepping each other on writing CertValidated and TimeStamp here.
|
|
// We are checking the validation in hours, a fraction of a second window here can be ignored.
|
|
//
|
|
|
|
if (CurrentRecoveryPolicy.dwKeyCount && fResult) {
|
|
CurrentRecoveryPolicy.CertValidated = CERT_VALIDATED;
|
|
} else if ( CurrentRecoveryPolicy.dwKeyCount ) {
|
|
CurrentRecoveryPolicy.CertValidated = CERT_VALIDATION_FAILED;
|
|
}
|
|
|
|
if (CurrentRecoveryPolicy.CertValidated != CERT_NOT_VALIDATED) {
|
|
CurrentRecoveryPolicy.TimeStamp.QuadPart = TimeStamp.QuadPart;
|
|
}
|
|
|
|
if (IsCertBeingValidated != 1) {
|
|
InterlockedExchange(&RecoveryCertIsValidating, IsCertBeingValidated);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (CurrentRecoveryPolicy.CertValidated == CERT_VALIDATED) {
|
|
|
|
*dwKeyCount = CurrentRecoveryPolicy.dwKeyCount;
|
|
*pbPublicKeys = CurrentRecoveryPolicy.pbPublicKeys;
|
|
*cbPublicKeys = CurrentRecoveryPolicy.cbPublicKeys;
|
|
*pbHashes = CurrentRecoveryPolicy.pbHash;
|
|
*cbHashes = CurrentRecoveryPolicy.cbHash;
|
|
|
|
if (lpDisplayInfo) {
|
|
*lpDisplayInfo = CurrentRecoveryPolicy.lpDisplayInfo;
|
|
}
|
|
|
|
if (pSid) {
|
|
*pSid = CurrentRecoveryPolicy.pSid;
|
|
}
|
|
|
|
} else {
|
|
|
|
*dwKeyCount = 0;
|
|
}
|
|
|
|
if ( ((RECOVERY_POLICY_EXPIRED_CERTS == *dwPolicyStatus) ||
|
|
(RECOVERY_POLICY_NOT_EFFECT_CERTS == *dwPolicyStatus)) &&
|
|
( 0 == MissingRecoveryPolicyLogged) ) {
|
|
|
|
DWORD eventID = EFS_INVALID_RECOVERY_POLICY_ERROR;
|
|
|
|
//
|
|
// Log the fail to get the recovery policy
|
|
//
|
|
|
|
MissingRecoveryPolicyLogged = 1;
|
|
|
|
EfsLogEntry(
|
|
EVENTLOG_ERROR_TYPE,
|
|
0,
|
|
eventID,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
return( ERROR_SUCCESS );
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
ConstructEFS(
|
|
PEFS_USER_INFO pEfsUserInfo,
|
|
PEFS_KEY Fek,
|
|
PEFS_DATA_STREAM_HEADER ParentEfsStreamHeader,
|
|
PEFS_DATA_STREAM_HEADER * EfsStreamHeader
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will construct an EFS stream. It is intended to be used
|
|
whenever an entire EFS stream is required, such as when a new file is
|
|
created.
|
|
|
|
An EFS stream contains a header, a DDF (which contains current user key
|
|
information), and a DRF (which contains recovery information).
|
|
|
|
Arguments:
|
|
|
|
Fek - Supplies a pointer to a partially filled in EFS_KEY structure,
|
|
specifying the length of the desired key and the algorithm that
|
|
will be used with the key to encrypt the file.
|
|
|
|
It is important that the algorithm field be filled in, since this
|
|
key will be eventually encrypted in its entirety, and all the fields
|
|
must be present for that to work.
|
|
|
|
ParentEfsStreamHeader - Supplies the EFS stream from the containing directory,
|
|
if one exists. This parameter is not currently used, because we do
|
|
not support inheritance from directories to files (yet).
|
|
|
|
EfsStreamHeader - Returns a pointer to an EFS_DATA_STREAM_HEADER which is
|
|
the head of an EFS stream. This header is followed by variable length
|
|
data containing the actual EFS data.
|
|
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
|
|
LPWSTR ContainerName = NULL;
|
|
HCRYPTPROV hProv = 0;
|
|
HCRYPTKEY hUserKey = 0;
|
|
LPWSTR lpDisplayInformation = NULL;
|
|
LPWSTR ProviderName = NULL;
|
|
DWORD ProviderType = 0;
|
|
PUCHAR PublicKey = NULL;
|
|
HCRYPTKEY hWkUserKey = NULL;
|
|
DWORD rc;
|
|
|
|
PEFS_DATA_STREAM_HEADER EFS = NULL;
|
|
|
|
DWORD DRFLength = 0;
|
|
DWORD DDFLength = 0;
|
|
|
|
PENCRYPTED_KEYS pDRF = NULL;
|
|
PENCRYPTED_KEYS pDDF = NULL;
|
|
|
|
PBYTE pbHash;
|
|
DWORD cbHash;
|
|
|
|
BOOLEAN b = FALSE;
|
|
|
|
//
|
|
// To build the DDF, we need the user's current key from the registry.
|
|
// This routine will get the key information from the registry and open
|
|
// the context containing the key.
|
|
//
|
|
|
|
rc = GetCurrentKey(
|
|
pEfsUserInfo,
|
|
&hUserKey,
|
|
&hProv,
|
|
&ContainerName,
|
|
&ProviderName,
|
|
&ProviderType,
|
|
&lpDisplayInformation,
|
|
&pbHash,
|
|
&cbHash
|
|
);
|
|
|
|
if (ERROR_SUCCESS == rc) {
|
|
|
|
if (hUserKey) {
|
|
hWkUserKey = hUserKey;
|
|
} else {
|
|
|
|
//
|
|
// Use the key in the cache
|
|
//
|
|
|
|
ASSERT(pEfsUserInfo->pUserCache);
|
|
ASSERT(pEfsUserInfo->pUserCache->hUserKey);
|
|
|
|
hWkUserKey = pEfsUserInfo->pUserCache->hUserKey;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
SetLastError( rc );
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// Before we exit, make sure to clean up ContainerName, ProviderName, pbHash, hUserKey, hProv
|
|
//
|
|
|
|
rc = GenerateDRF( Fek, &pDRF, &DRFLength);
|
|
|
|
if (ERROR_SUCCESS == rc) {
|
|
|
|
LPWSTR lpWkContainerName;
|
|
LPWSTR lpWkDisplayInformation;
|
|
LPWSTR lpWkProviderName;
|
|
PBYTE pbWkHash;
|
|
DWORD cbWkHash;
|
|
|
|
if (hUserKey) {
|
|
|
|
//
|
|
// Do not use the cache
|
|
//
|
|
|
|
lpWkContainerName = ContainerName;
|
|
lpWkProviderName = ProviderName;
|
|
lpWkDisplayInformation = lpDisplayInformation;
|
|
pbWkHash = pbHash;
|
|
cbWkHash = cbHash;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Use the cache
|
|
//
|
|
|
|
lpWkContainerName = pEfsUserInfo->pUserCache->ContainerName;
|
|
lpWkProviderName = pEfsUserInfo->pUserCache->ProviderName;
|
|
lpWkDisplayInformation = pEfsUserInfo->pUserCache->DisplayInformation;
|
|
pbWkHash = pEfsUserInfo->pUserCache->pbHash;
|
|
cbWkHash = pEfsUserInfo->pUserCache->cbHash;
|
|
|
|
}
|
|
|
|
rc = ConstructKeyRing(
|
|
Fek,
|
|
1,
|
|
&lpWkContainerName,
|
|
&lpWkProviderName,
|
|
(PBYTE *)&hWkUserKey,
|
|
NULL,
|
|
&pbWkHash,
|
|
&cbWkHash,
|
|
&lpWkDisplayInformation,
|
|
&(pEfsUserInfo->pTokenUser->User.Sid),
|
|
TRUE,
|
|
&pDDF,
|
|
&DDFLength
|
|
);
|
|
|
|
if (ERROR_SUCCESS == rc) {
|
|
|
|
DWORD EfsLength = DDFLength + DRFLength + sizeof( EFS_DATA_STREAM_HEADER );
|
|
|
|
//
|
|
// Efs has to be a multiple of 8 in length to encrypt properly.
|
|
//
|
|
|
|
EfsLength = (EfsLength + 7) & 0xfffffff8;
|
|
|
|
EFS = (PEFS_DATA_STREAM_HEADER)LsapAllocateLsaHeap( EfsLength );
|
|
|
|
if (EFS != NULL) {
|
|
|
|
memset( EFS, 0, sizeof( EFS_DATA_STREAM_HEADER ));
|
|
|
|
EFS->Length = EfsLength;
|
|
EFS->State = 0; // used by the server
|
|
EFS->EfsVersion = EFS_CURRENT_VERSION;
|
|
|
|
RPC_STATUS RpcStatus = UuidCreate ( &EFS->EfsId );
|
|
|
|
if (RpcStatus == ERROR_SUCCESS || RpcStatus == RPC_S_UUID_LOCAL_ONLY) {
|
|
|
|
//
|
|
// A "local-only" UUID is ok in this case
|
|
//
|
|
|
|
EFS->DataDecryptionField = (ULONG)sizeof(EFS_DATA_STREAM_HEADER );
|
|
memcpy( (PENCRYPTED_KEYS)((PBYTE)EFS + sizeof( EFS_DATA_STREAM_HEADER )), pDDF, DDFLength );
|
|
|
|
if ( 0 == DRFLength) {
|
|
EFS->DataRecoveryField = 0;
|
|
} else {
|
|
EFS->DataRecoveryField = (ULONG)(sizeof(EFS_DATA_STREAM_HEADER ) + DDFLength);
|
|
memcpy( (PENCRYPTED_KEYS)((PBYTE)EFS + sizeof( EFS_DATA_STREAM_HEADER ) + DDFLength ), pDRF, DRFLength );
|
|
}
|
|
|
|
/*
|
|
BOOLEAN f = EfspChecksumEfs( EFS, Fek );
|
|
|
|
ASSERT( f );
|
|
|
|
if (!f) {
|
|
rc = GetLastError();
|
|
ASSERT( rc != ERROR_SUCCESS );
|
|
LsapFreeLsaHeap( EFS );
|
|
*EfsStreamHeader = NULL;
|
|
} else {
|
|
|
|
//
|
|
// Everything worked, return success.
|
|
//
|
|
|
|
*EfsStreamHeader = EFS;
|
|
b = TRUE;
|
|
}
|
|
*/
|
|
|
|
*EfsStreamHeader = EFS;
|
|
b = TRUE;
|
|
|
|
|
|
}
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
ReleaseRecoveryData();
|
|
|
|
if (pDDF) {
|
|
|
|
LsapFreeLsaHeap(pDDF);
|
|
|
|
}
|
|
|
|
if (pDRF) {
|
|
|
|
LsapFreeLsaHeap(pDRF);
|
|
}
|
|
|
|
|
|
if (ContainerName) {
|
|
|
|
//
|
|
// Defensive checking
|
|
//
|
|
|
|
LsapFreeLsaHeap( ContainerName );
|
|
}
|
|
|
|
if (ProviderName) {
|
|
LsapFreeLsaHeap( ProviderName );
|
|
}
|
|
|
|
if (lpDisplayInformation) {
|
|
LsapFreeLsaHeap( lpDisplayInformation );
|
|
}
|
|
|
|
if (PublicKey) {
|
|
LsapFreeLsaHeap( PublicKey );
|
|
}
|
|
|
|
if (pbHash) {
|
|
LsapFreeLsaHeap( pbHash );
|
|
}
|
|
|
|
if (hUserKey) {
|
|
CryptDestroyKey( hUserKey );
|
|
}
|
|
if (hProv) {
|
|
CryptReleaseContext( hProv, 0 );
|
|
}
|
|
|
|
if (EFSDebugLevel > 0) {
|
|
DumpEFS( *EfsStreamHeader );
|
|
}
|
|
|
|
SetLastError( rc );
|
|
|
|
|
|
#if DBG
|
|
|
|
if (!b) {
|
|
ASSERT( rc != ERROR_SUCCESS );
|
|
}
|
|
|
|
#endif
|
|
|
|
return( b );
|
|
|
|
}
|
|
|
|
DWORD
|
|
CopyEfsStream(
|
|
OUT PEFS_DATA_STREAM_HEADER * Target,
|
|
IN PEFS_DATA_STREAM_HEADER Source
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Makes a copy of the passed EFS stream. Allocates memory for the target
|
|
which must be freed.
|
|
|
|
Arguments:
|
|
|
|
Target - Takes a pointer which is filled in with a pointer to the copy
|
|
of the EFS stream. This pointer must be freed.
|
|
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS, or ERROR_NOT_ENOUGH_MEMORY if we can't allocate memory for the
|
|
target buffer.
|
|
|
|
--*/
|
|
|
|
{
|
|
*Target = (PEFS_DATA_STREAM_HEADER)LsapAllocateLsaHeap( Source->Length );
|
|
|
|
if (*Target) {
|
|
memcpy( *Target, Source, Source->Length );
|
|
} else {
|
|
return( ERROR_NOT_ENOUGH_MEMORY );
|
|
}
|
|
|
|
return( ERROR_SUCCESS );
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
ConstructDirectoryEFS(
|
|
IN PEFS_USER_INFO pEfsUserInfo,
|
|
IN PEFS_KEY Fek,
|
|
OUT PEFS_DATA_STREAM_HEADER * EfsStreamHeader
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine constructs the EFS stream for a directory.
|
|
|
|
Arguments:
|
|
|
|
pEfsUserInfo - Supplies useful information about our caller.
|
|
|
|
Fek - Supplies the Fek to put into the EFS stream.
|
|
|
|
EfsStreamHeader - Returns a pointer to the new EFS stream.
|
|
|
|
|
|
Return Value:
|
|
|
|
return-value - Description of conditions needed to return value. - or -
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
return( ConstructEFS( pEfsUserInfo, Fek, NULL, EfsStreamHeader ) );
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
ConstructKeyRing(
|
|
IN PEFS_KEY Fek,
|
|
IN DWORD KeyCount,
|
|
IN LPWSTR KeyNames[] OPTIONAL,
|
|
IN LPWSTR ProviderNames[] OPTIONAL,
|
|
IN PBYTE PublicKeys[],
|
|
IN DWORD PublicKeyLengths[],
|
|
IN PBYTE pbHashes[],
|
|
IN DWORD cbHashes[],
|
|
IN LPWSTR lpDisplayInformation[],
|
|
IN PSID pSid[],
|
|
IN BOOLEAN PublicKeyHandle,
|
|
OUT PENCRYPTED_KEYS *KeyRing,
|
|
OUT PDWORD KeyRingLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will construct a key ring (DDF or DRF) structure. A keyring
|
|
contains one or more ENCRYPTED_KEY structures, each of which represents
|
|
an encoding of the FEK with a different user key.
|
|
|
|
The caller is expected to call this routine twice, once to determine the
|
|
length of the structure, and a second time to actually create the key ring
|
|
structure.
|
|
|
|
Note that the passed keys do not need to exist in the current context,
|
|
and if we are building a DRF structure, most likely will not exist
|
|
in the current context.
|
|
|
|
Arguments:
|
|
|
|
Fek - Provides the unencrypted FEK for the file.
|
|
|
|
KeyCount - Provides the number of keys that are going to be placed in this
|
|
keyring.
|
|
|
|
KeyNames - Provides an array of NULL-terminated WCHAR strings, each naming
|
|
a key.
|
|
|
|
ProviderNames - Provides an array of providers that is parallel to the
|
|
KeyNames array.
|
|
|
|
PublicKeys - Provides an array of pointers to PUBLICKEYBLOB structures,
|
|
one for each named key.
|
|
|
|
PublicKeyLengths - Provides an array of lengths of the PUBLICKEYBLOB
|
|
structures pointed to by the PublicKeys array. It could also points to the key
|
|
handle.
|
|
|
|
pSid - Users' SIDs
|
|
|
|
PublicKeyHandle - Indicate if PublicKeys point to PUBLICKEYBLOB or key handles.
|
|
|
|
KeyRing - Returns a pointer to the constructed keyring. If this parameter
|
|
is NULL, only the length will be computed and returned.
|
|
|
|
KeyRingLength - Provides the size of the passed KeyRing buffer, or or if the
|
|
KeyRing pointer is NULL, the size of the buffer that must be passed in to
|
|
return the KeyRing.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS - Returned if successful.
|
|
|
|
|
|
ERROR_NOT_ENOUGH_MEMORY - Some attempt to allocate memory from the local
|
|
heap failed.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// For each Key passed in, import the public key blob
|
|
// and export the session key encrypted with that blob.
|
|
// The FEK will be encrypted with the same session key
|
|
// in each entry.
|
|
//
|
|
|
|
PEFS_KEY_SALT pEfsKeySalt = NULL;
|
|
|
|
PENCRYPTED_KEY * EncryptedKey = NULL;
|
|
BOOL GotPublicKey = TRUE;
|
|
|
|
EncryptedKey = (PENCRYPTED_KEY *)LsapAllocateLsaHeap( KeyCount * sizeof(PENCRYPTED_KEY) );
|
|
|
|
*KeyRing = NULL;
|
|
|
|
if (EncryptedKey == NULL) {
|
|
return( ERROR_NOT_ENOUGH_MEMORY );
|
|
}
|
|
|
|
PDWORD EncryptedKeySize = (PDWORD)LsapAllocateLsaHeap( KeyCount * sizeof( DWORD ));
|
|
|
|
if (EncryptedKeySize == NULL) {
|
|
|
|
LsapFreeLsaHeap( EncryptedKey );
|
|
return( ERROR_NOT_ENOUGH_MEMORY );
|
|
}
|
|
|
|
DWORD i;
|
|
|
|
DWORD rc = ERROR_SUCCESS;
|
|
|
|
for (i = 0 ; i<KeyCount ; i++) {
|
|
|
|
EncryptedKey[i] = NULL;
|
|
EncryptedKeySize[i] = 0;
|
|
}
|
|
|
|
for ( i = 0; i<KeyCount && rc==ERROR_SUCCESS ; i++ ) {
|
|
|
|
//
|
|
// Import the passed public key
|
|
//
|
|
|
|
HCRYPTKEY hXchgKey = 0;
|
|
|
|
if (PublicKeyHandle) {
|
|
|
|
GotPublicKey = TRUE;
|
|
hXchgKey = (HCRYPTKEY)PublicKeys[i];
|
|
|
|
} else {
|
|
GotPublicKey = CryptImportKey( hProvVerify, PublicKeys[i], PublicKeyLengths[i], 0, CRYPT_EXPORTABLE, &hXchgKey );
|
|
}
|
|
|
|
if (GotPublicKey) {
|
|
|
|
DWORD dwEncryptedFEKLength = 0;
|
|
|
|
PBYTE EncryptedFEK = EncryptFEK( Fek, hXchgKey, &dwEncryptedFEKLength );
|
|
|
|
if (EncryptedFEK == NULL) {
|
|
|
|
rc = GetLastError();
|
|
ASSERT( rc != ERROR_SUCCESS );
|
|
|
|
} else {
|
|
|
|
PEFS_PUBLIC_KEY_INFO PublicKeyInformation = NULL;
|
|
|
|
LPWSTR KeyName;
|
|
LPWSTR ProviderName;
|
|
|
|
if (KeyNames && ProviderNames) {
|
|
|
|
KeyName = KeyNames[i];
|
|
ProviderName = ProviderNames[i];
|
|
|
|
} else {
|
|
|
|
KeyName = NULL;
|
|
ProviderName = NULL;
|
|
}
|
|
|
|
if (CreatePublicKeyInformationThumbprint(
|
|
pSid[i],
|
|
pbHashes[i],
|
|
cbHashes[i],
|
|
lpDisplayInformation[i],
|
|
KeyName,
|
|
ProviderName,
|
|
&PublicKeyInformation
|
|
)) {
|
|
|
|
if ( Fek->Entropy <= EXPORT_KEY_STRENGTH ){
|
|
|
|
DWORD SaltLength;
|
|
DWORD SaltBlockLength;
|
|
|
|
if (GetSaltLength(Fek->Algorithm, &SaltLength, &SaltBlockLength)){
|
|
|
|
pEfsKeySalt = (PEFS_KEY_SALT)LsapAllocateLsaHeap( sizeof( EFS_KEY_SALT ) + SaltBlockLength );
|
|
if (pEfsKeySalt){
|
|
pEfsKeySalt->Length = sizeof( EFS_KEY_SALT ) + SaltBlockLength;
|
|
pEfsKeySalt->SaltType = Fek->Algorithm;
|
|
RtlCopyMemory( (PBYTE)pEfsKeySalt + sizeof( EFS_KEY_SALT ),
|
|
EFS_KEY_DATA( Fek ),
|
|
SaltLength
|
|
);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
pEfsKeySalt = NULL;
|
|
}
|
|
|
|
if (pEfsKeySalt || (Fek->Entropy > EXPORT_KEY_STRENGTH)) {
|
|
|
|
|
|
rc = ConstructEncryptedKey( EncryptedFEK,
|
|
dwEncryptedFEKLength,
|
|
PublicKeyInformation,
|
|
pEfsKeySalt,
|
|
&EncryptedKey[i],
|
|
&EncryptedKeySize[i]
|
|
);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Clean up output from CreatePublicKeyInformation
|
|
//
|
|
|
|
LsapFreeLsaHeap( PublicKeyInformation );
|
|
if (pEfsKeySalt){
|
|
LsapFreeLsaHeap( pEfsKeySalt );
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
}
|
|
|
|
//
|
|
// Clean up output from EncryptFEK
|
|
//
|
|
|
|
LsapFreeLsaHeap( EncryptedFEK );
|
|
}
|
|
|
|
//
|
|
// If the we imported the key, don't need this key any more, get rid of it.
|
|
//
|
|
|
|
if (!PublicKeyHandle) {
|
|
CryptDestroyKey( hXchgKey );
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Couldn't import a public key, pick up error code
|
|
//
|
|
|
|
rc = GetLastError();
|
|
}
|
|
|
|
|
|
if (rc != ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Something failed along the way, clean up all previous allocations
|
|
//
|
|
|
|
for (DWORD j = 0; j < i ; j++ ) {
|
|
if (EncryptedKey[j]) {
|
|
LsapFreeLsaHeap( EncryptedKey[j] );
|
|
}
|
|
}
|
|
|
|
LsapFreeLsaHeap( EncryptedKey );
|
|
|
|
LsapFreeLsaHeap( EncryptedKeySize );
|
|
|
|
return( rc );
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// We successfully created all of the EncryptedKey structures. Assemble them
|
|
// all into a KeyRing and return the result.
|
|
//
|
|
|
|
|
|
*KeyRingLength = 0;
|
|
|
|
for (i=0 ; i<KeyCount ; i++) {
|
|
*KeyRingLength += EncryptedKeySize[i];
|
|
}
|
|
|
|
*KeyRingLength += (sizeof ( ENCRYPTED_KEYS ) - sizeof( ENCRYPTED_KEY ));
|
|
|
|
|
|
*KeyRing = (PENCRYPTED_KEYS)LsapAllocateLsaHeap( *KeyRingLength );
|
|
|
|
if (NULL != *KeyRing) {
|
|
|
|
(*KeyRing)->KeyCount = KeyCount;
|
|
|
|
PBYTE Base = (PBYTE) &((*KeyRing)->EncryptedKey[0]);
|
|
|
|
for (i=0 ; i<KeyCount ; i++) {
|
|
|
|
memcpy( Base, EncryptedKey[i], EncryptedKey[i]->Length );
|
|
Base += EncryptedKey[i]->Length;
|
|
}
|
|
|
|
} else {
|
|
*KeyRingLength = 0;
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Clean everything up and return
|
|
//
|
|
|
|
for (i = 0; i<KeyCount ; i++ ) {
|
|
LsapFreeLsaHeap( EncryptedKey[i] );
|
|
}
|
|
|
|
LsapFreeLsaHeap( EncryptedKey );
|
|
|
|
LsapFreeLsaHeap( EncryptedKeySize );
|
|
|
|
return( rc );
|
|
}
|
|
|
|
|
|
PEFS_KEY
|
|
GetFekFromEncryptedKeys(
|
|
IN OUT PEFS_USER_INFO pEfsUserInfo,
|
|
IN PENCRYPTED_KEYS Keys,
|
|
IN BOOL CheckBits,
|
|
OUT PDWORD KeyIndex
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will attempt to decode the FEK from an ENCRYPTED_KEYS
|
|
structure. It will do this by iterating through all of the fields in the
|
|
DRF and attempting to use each one to decrypt the FEK.
|
|
|
|
Arguments:
|
|
|
|
pEfsUserInfo - User information.
|
|
|
|
Keys - Provides the ENCRYPTED_KEYS to be examined.
|
|
|
|
CheckBits - If we need to check international version or not. TRUE will check.
|
|
|
|
KeyIndex - Which encrypted key is used.
|
|
|
|
Return Value:
|
|
|
|
On success, returns a pointer to an FEK, which must be freed when no longer
|
|
needed. Returns NULL on error.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Walk down the list of key names in the ENCRYTPED_KEYS
|
|
//
|
|
|
|
if (Keys != NULL) {
|
|
|
|
PENCRYPTED_KEY pEncryptedKey = &Keys->EncryptedKey[0];
|
|
ULONG keyCount = *(ULONG UNALIGNED*)&(Keys->KeyCount);
|
|
|
|
for (*KeyIndex=0 ; *KeyIndex<keyCount ; (*KeyIndex)++) {
|
|
|
|
PENCRYPTED_KEY pAlignedKey;
|
|
BOOLEAN freeAlignedKey;
|
|
DWORD retCode;
|
|
|
|
retCode = EfsAlignBlock(
|
|
pEncryptedKey,
|
|
(PVOID *)&pAlignedKey,
|
|
&freeAlignedKey
|
|
);
|
|
if (!pAlignedKey) {
|
|
|
|
//
|
|
// OOM. Treat it as not current.
|
|
//
|
|
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return NULL;
|
|
|
|
}
|
|
|
|
PEFS_KEY Fek = ExtractFek( pEfsUserInfo, pAlignedKey, CheckBits );
|
|
|
|
if (Fek != NULL) {
|
|
|
|
//
|
|
// Decryption worked, return the key
|
|
//
|
|
|
|
if (freeAlignedKey) {
|
|
LsapFreeLsaHeap( pAlignedKey );
|
|
}
|
|
return( Fek );
|
|
}
|
|
|
|
pEncryptedKey = (PENCRYPTED_KEY)( ((PBYTE)pEncryptedKey) + pAlignedKey->Length );
|
|
if (freeAlignedKey) {
|
|
LsapFreeLsaHeap( pAlignedKey );
|
|
}
|
|
}
|
|
}
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
|
|
DWORD
|
|
GetLengthEncryptedKeys(
|
|
IN PENCRYPTED_KEYS pEncryptedKeys
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Computes the total size in bytes of an ENCRYPTED_KEYS structure.
|
|
|
|
Arguments:
|
|
|
|
pEncryptedKeys - Supplies a pointer to an ENCRYPTED_KEYS structre.
|
|
|
|
Return Value:
|
|
|
|
The length in bytes of the passed structure.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD cb=0;
|
|
ULONG keyCount = *((ULONG UNALIGNED *) &(pEncryptedKeys->KeyCount));
|
|
ULONG keyLength;
|
|
|
|
PENCRYPTED_KEY pEncryptedKey = &pEncryptedKeys->EncryptedKey[0];
|
|
|
|
for (DWORD i=0; i<keyCount ; i++) {
|
|
|
|
keyLength = *((ULONG UNALIGNED *) &(pEncryptedKey->Length));
|
|
cb += keyLength;
|
|
pEncryptedKey = (PENCRYPTED_KEY)( ((PBYTE)pEncryptedKey) + keyLength );
|
|
}
|
|
|
|
cb += sizeof( ENCRYPTED_KEYS ) - sizeof( ENCRYPTED_KEY );
|
|
|
|
return( cb );
|
|
}
|
|
|
|
BOOL
|
|
AppendEncryptedKeyToDDF(
|
|
IN PEFS_DATA_STREAM_HEADER EfsStream,
|
|
IN PENCRYPTED_KEY EncryptedKey,
|
|
IN PEFS_KEY Fek,
|
|
OUT PEFS_DATA_STREAM_HEADER * OutputEfs
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will take an existing EFS stream and append the
|
|
passed encrypted key to the end of the DDF section. It does
|
|
not check to see if the key is already there or not.
|
|
|
|
Arguments:
|
|
|
|
EfsStream - The existing EFS stream.
|
|
|
|
EncryptedKey - The FEK encrypted with the new public key.
|
|
|
|
OutputEfs - Receives the new EFS stream to be placed on the
|
|
file.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL b = FALSE;
|
|
|
|
//
|
|
// This is a simple append operation.
|
|
//
|
|
// The new size is the size of the old EFS stream
|
|
// plus the size of the new key. Allocate space for it.
|
|
//
|
|
|
|
DWORD EfsLength = EfsStream->Length + EncryptedKey->Length;
|
|
EfsLength = (EfsLength + 7) & 0xfffffff8;
|
|
|
|
*OutputEfs = (PEFS_DATA_STREAM_HEADER)LsapAllocateLsaHeap( EfsLength );
|
|
|
|
if (*OutputEfs) {
|
|
|
|
memset( *OutputEfs, 0, sizeof( EFS_DATA_STREAM_HEADER ));
|
|
|
|
//
|
|
// Copy the header
|
|
//
|
|
|
|
PEFS_DATA_STREAM_HEADER Efs = *OutputEfs;
|
|
*Efs = *EfsStream;
|
|
|
|
Efs->Length = EfsLength;
|
|
|
|
//
|
|
// Start copying the DDF at the base of the EFS
|
|
// structure. Copy the whole thing. Do ourselves a
|
|
// favor and don't assume that the DDF or DRF are in
|
|
// any particular order in the EFS structure.
|
|
//
|
|
|
|
PENCRYPTED_KEYS pDDF = (PENCRYPTED_KEYS)OFFSET_TO_POINTER( DataDecryptionField, EfsStream );
|
|
DWORD cbDDF = GetLengthEncryptedKeys( pDDF );
|
|
|
|
//
|
|
// Store away the offset to the beginning on the DDF
|
|
//
|
|
|
|
Efs->DataDecryptionField = (ULONG)sizeof( EFS_DATA_STREAM_HEADER );
|
|
|
|
PBYTE Base = (PBYTE)OFFSET_TO_POINTER( DataDecryptionField, Efs );
|
|
memcpy( Base, pDDF, cbDDF );
|
|
|
|
//
|
|
// Point to the new DDF, we need to fix it up a little
|
|
//
|
|
|
|
PENCRYPTED_KEYS pNewDDF = (PENCRYPTED_KEYS)Base;
|
|
pNewDDF->KeyCount++;
|
|
|
|
Base += cbDDF;
|
|
|
|
memcpy( Base, EncryptedKey, EncryptedKey->Length );
|
|
|
|
Base += EncryptedKey->Length;
|
|
|
|
//
|
|
// Now copy the DRF onto the end and we're done.
|
|
//
|
|
|
|
|
|
PENCRYPTED_KEYS pDRF = (PENCRYPTED_KEYS)OFFSET_TO_POINTER( DataRecoveryField, EfsStream );
|
|
|
|
if ((PVOID) pDRF == (PVOID) EfsStream) {
|
|
|
|
Efs->DataRecoveryField = 0;
|
|
|
|
} else {
|
|
|
|
DWORD cbDRF = GetLengthEncryptedKeys( pDRF );
|
|
Efs->DataRecoveryField = (ULONG)POINTER_TO_OFFSET( Base, Efs );
|
|
memcpy( Base, pDRF, cbDRF );
|
|
|
|
}
|
|
|
|
|
|
// Base += cbDRF
|
|
|
|
b = TRUE;
|
|
|
|
// memset( &(Efs->EfsHash), 0, MD5_HASH_SIZE );
|
|
// EfspChecksumEfs( Efs, Fek );
|
|
|
|
} else {
|
|
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
}
|
|
|
|
return( b );
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
EfspCertInEFS(
|
|
IN PBYTE pbHash,
|
|
IN DWORD cbHash,
|
|
IN PEFS_DATA_STREAM_HEADER pEfsStream
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Searches the passed EFS stream for an entry with the same hash as passed.
|
|
|
|
Arguments:
|
|
|
|
pbHash - Supplies a pointer to the hash being queried
|
|
|
|
cbHash - Supplies the length in bytes of the hash being queried
|
|
|
|
pEfsStream - Supplies the EFS stream from the file being queried
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE if the passed hash is found, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN fFound = FALSE;
|
|
DWORD KeyIndex;
|
|
|
|
//
|
|
// Check the hash in each entry in the DDF. If
|
|
// we get a match, return success.
|
|
//
|
|
|
|
PDDF Keys = (PENCRYPTED_KEYS)OFFSET_TO_POINTER( DataDecryptionField, pEfsStream );
|
|
|
|
PENCRYPTED_KEY pEncryptedKey = &Keys->EncryptedKey[0];
|
|
|
|
for (KeyIndex=0 ; KeyIndex<Keys->KeyCount ; KeyIndex++) {
|
|
|
|
PEFS_PUBLIC_KEY_INFO PublicKeyInfo = (PEFS_PUBLIC_KEY_INFO)( (PUCHAR)pEncryptedKey + *(ULONG UNALIGNED *) &(pEncryptedKey->PublicKeyInfo) );
|
|
PEFS_CERT_HASH_DATA CertHashData = (PEFS_CERT_HASH_DATA)( (PUCHAR) PublicKeyInfo + *(ULONG UNALIGNED *) &(PublicKeyInfo->CertificateThumbprint.CertHashData));
|
|
|
|
if (*(DWORD UNALIGNED *)&(CertHashData->cbHash) == cbHash) {
|
|
|
|
PBYTE pbSrcHash = (PBYTE)CertHashData + *(ULONG UNALIGNED *) &(CertHashData->pbHash);
|
|
|
|
if (memcmp(pbSrcHash, pbHash, cbHash) == 0) {
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pEncryptedKey = NEXT_ENCRYPTED_KEY( pEncryptedKey );
|
|
}
|
|
|
|
return( fFound );
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
AddUserToEFS(
|
|
IN PEFS_DATA_STREAM_HEADER EfsStream,
|
|
IN PSID NewUserSid OPTIONAL,
|
|
IN PEFS_KEY Fek,
|
|
IN PBYTE pbCert,
|
|
IN DWORD cbCert,
|
|
OUT PEFS_DATA_STREAM_HEADER * NewEfs
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine adds a new encrypted key block to the DDF of the passed
|
|
EFS stream.
|
|
|
|
Arguments:
|
|
|
|
EfsStream - Takes a pointer to the EFS stream to be modified.
|
|
|
|
NewUserSid - Optionally supplies a pointer to the SID of the new user.
|
|
|
|
Fek - Supplies the FEK of the file being modified.
|
|
|
|
pbCert - Supplies a pointer to the certificate of the new user.
|
|
|
|
cbCert - Supplies the lenght in bytes of the certificate.
|
|
|
|
NewEfs - Returns a pointer to the new EFS stream.
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure. Call GetLastError() for more details.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD rc;
|
|
PEFS_DATA_STREAM_HEADER Efs = NULL;
|
|
BOOLEAN b = FALSE;
|
|
PEFS_KEY_SALT pEfsKeySalt = NULL;
|
|
|
|
//
|
|
// Get the key information from the passed cert
|
|
//
|
|
|
|
PCCERT_CONTEXT pCertContext = CertCreateCertificateContext(
|
|
X509_ASN_ENCODING,
|
|
pbCert,
|
|
cbCert
|
|
);
|
|
if (pCertContext != NULL) {
|
|
|
|
PBYTE pbHash;
|
|
DWORD cbHash;
|
|
|
|
pbHash = GetCertHashFromCertContext(
|
|
pCertContext,
|
|
&cbHash
|
|
);
|
|
|
|
if (pbHash) {
|
|
|
|
//
|
|
// See if this hash is already on the file. If so, return error.
|
|
//
|
|
|
|
if (!EfspCertInEFS( pbHash, cbHash, EfsStream )) {
|
|
|
|
//
|
|
// Now get the public key out of the cert so we can
|
|
// encrypt the FEK
|
|
//
|
|
|
|
PCERT_PUBLIC_KEY_INFO pSubjectPublicKeyInfo = &pCertContext->pCertInfo->SubjectPublicKeyInfo;
|
|
|
|
//
|
|
// Import the public key into a context
|
|
//
|
|
|
|
HCRYPTKEY hKey;
|
|
|
|
if (CryptImportPublicKeyInfo( hProvVerify, X509_ASN_ENCODING, pSubjectPublicKeyInfo, &hKey )) {
|
|
|
|
//
|
|
// Use the newly imported key to encrypt the FEK
|
|
//
|
|
|
|
DWORD dwEncryptedFEKLength = 0;
|
|
|
|
PBYTE EncryptedFEK = EncryptFEK( Fek, hKey, &dwEncryptedFEKLength );
|
|
|
|
if (EncryptedFEK != NULL) {
|
|
|
|
PEFS_PUBLIC_KEY_INFO PublicKeyInformation = NULL;
|
|
|
|
//
|
|
// This may come back NULL, but that's ok.
|
|
//
|
|
|
|
LPWSTR lpDisplayName = EfspGetCertDisplayInformation( pCertContext );
|
|
|
|
b = CreatePublicKeyInformationThumbprint(
|
|
NewUserSid,
|
|
pbHash,
|
|
cbHash,
|
|
lpDisplayName,
|
|
NULL,
|
|
NULL,
|
|
&PublicKeyInformation
|
|
);
|
|
|
|
if (lpDisplayName) {
|
|
LsapFreeLsaHeap( lpDisplayName );
|
|
}
|
|
|
|
if (b) {
|
|
|
|
if (Fek->Entropy <= EXPORT_KEY_STRENGTH) {
|
|
|
|
DWORD SaltLength;
|
|
DWORD SaltBlockLength;
|
|
|
|
if (GetSaltLength(Fek->Algorithm, &SaltLength, &SaltBlockLength)) {
|
|
|
|
pEfsKeySalt = (PEFS_KEY_SALT)LsapAllocateLsaHeap( sizeof( EFS_KEY_SALT ) + SaltBlockLength );
|
|
if (pEfsKeySalt) {
|
|
pEfsKeySalt->Length = sizeof( EFS_KEY_SALT ) + SaltBlockLength;
|
|
pEfsKeySalt->SaltType = Fek->Algorithm;
|
|
RtlCopyMemory( (PBYTE)pEfsKeySalt + sizeof( EFS_KEY_SALT ),
|
|
EFS_KEY_DATA( Fek ),
|
|
SaltLength
|
|
);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
pEfsKeySalt = NULL;
|
|
}
|
|
|
|
if (pEfsKeySalt || (Fek->Entropy > EXPORT_KEY_STRENGTH)) {
|
|
|
|
|
|
DWORD EncryptedKeySize = 0;
|
|
PENCRYPTED_KEY EncryptedKey;
|
|
|
|
rc = ConstructEncryptedKey( EncryptedFEK,
|
|
dwEncryptedFEKLength,
|
|
PublicKeyInformation,
|
|
pEfsKeySalt,
|
|
&EncryptedKey,
|
|
&EncryptedKeySize
|
|
);
|
|
//
|
|
// We'll check the return code below
|
|
//
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
b = AppendEncryptedKeyToDDF(
|
|
EfsStream,
|
|
EncryptedKey,
|
|
Fek,
|
|
NewEfs
|
|
) != 0;
|
|
|
|
LsapFreeLsaHeap( EncryptedKey );
|
|
|
|
} else {
|
|
|
|
SetLastError( rc );
|
|
}
|
|
|
|
if (pEfsKeySalt) {
|
|
LsapFreeLsaHeap( pEfsKeySalt );
|
|
}
|
|
|
|
} else {
|
|
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
}
|
|
|
|
LsapFreeLsaHeap( PublicKeyInformation );
|
|
}
|
|
|
|
LsapFreeLsaHeap( EncryptedFEK );
|
|
}
|
|
|
|
CryptDestroyKey( hKey );
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Adding duplicate cert considered succeed
|
|
//
|
|
|
|
b = TRUE;
|
|
}
|
|
|
|
LsapFreeLsaHeap( pbHash );
|
|
}
|
|
|
|
CertFreeCertificateContext( pCertContext );
|
|
}
|
|
|
|
if (!b) {
|
|
|
|
//
|
|
// If we're not going to return success, clean up everything we were
|
|
// planning on returning.
|
|
//
|
|
|
|
if (*NewEfs != NULL) {
|
|
LsapFreeLsaHeap( *NewEfs );
|
|
}
|
|
}
|
|
|
|
return( b );
|
|
}
|
|
|
|
|
|
PENCRYPTED_KEY
|
|
GetEncryptedKeyByIndex(
|
|
PENCRYPTED_KEYS pEncryptedKeys,
|
|
DWORD KeyIndex
|
|
)
|
|
{
|
|
ASSERT( KeyIndex < *((ULONG UNALIGNED *)&(pEncryptedKeys->KeyCount)) );
|
|
|
|
PENCRYPTED_KEY pEncryptedKey = &pEncryptedKeys->EncryptedKey[0];
|
|
|
|
if (KeyIndex == 0) {
|
|
return( pEncryptedKey );
|
|
}
|
|
|
|
for (DWORD i=0; i<KeyIndex ; i++, pEncryptedKey = (PENCRYPTED_KEY)(((PBYTE)(pEncryptedKey)) + *(ULONG UNALIGNED *)&((PENCRYPTED_KEY)(pEncryptedKey))->Length)) ;
|
|
|
|
return( pEncryptedKey );
|
|
}
|
|
|
|
BOOL
|
|
UserKeyCurrent(
|
|
PEFS_USER_INFO pEfsUserInfo,
|
|
PDDF Ddf,
|
|
DWORD KeyIndex
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks to see if the key used to decrypt the file
|
|
is the user's current encryption key.
|
|
|
|
Arguments:
|
|
|
|
Ddf - Supplies the DDF of the file being accessed.
|
|
|
|
KeyIndex - Supplies the index of the key in the DDF that was
|
|
used to open the file.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the key used corresponds to the user's encryption key.
|
|
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL b = TRUE;
|
|
DWORD rc = ERROR_SUCCESS;
|
|
|
|
PBYTE pbCurrentKeyHash = NULL;
|
|
DWORD cbCurrentKeyHash;
|
|
PBYTE pbHash;
|
|
DWORD cbHash;
|
|
PBYTE pbWkHash = NULL;
|
|
|
|
//
|
|
// Compare the current user key with the contents
|
|
// of the specified key, and see if they're in sync.
|
|
//
|
|
|
|
PENCRYPTED_KEY pEncryptedKey = GetEncryptedKeyByIndex( Ddf, KeyIndex );
|
|
|
|
PENCRYPTED_KEY pAlignedKey;
|
|
BOOLEAN freeAlignedKey;
|
|
|
|
rc = EfsAlignBlock(
|
|
pEncryptedKey,
|
|
(PVOID *)&pAlignedKey,
|
|
&freeAlignedKey
|
|
);
|
|
if (!pAlignedKey) {
|
|
|
|
//
|
|
// OOM. Treat it as current.
|
|
//
|
|
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
PEFS_PUBLIC_KEY_INFO pPublicKeyInfo = (PEFS_PUBLIC_KEY_INFO)OFFSET_TO_POINTER( PublicKeyInfo, pAlignedKey );
|
|
|
|
if (pPublicKeyInfo->KeySourceTag != EfsCertificateThumbprint) {
|
|
|
|
//
|
|
// The user key may be current, but the key on the file isn't.
|
|
// Return FALSE so as to regenerate the EFS on this file.
|
|
//
|
|
|
|
DebugLog((DEB_WARN, "Updating downlevel file\n" ));
|
|
if (freeAlignedKey) {
|
|
LsapFreeLsaHeap( pAlignedKey );
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
PEFS_CERT_HASH_DATA CertHashData = (PEFS_CERT_HASH_DATA)OFFSET_TO_POINTER( CertificateThumbprint.CertHashData, pPublicKeyInfo );
|
|
pbHash = (PBYTE)OFFSET_TO_POINTER( pbHash, CertHashData );
|
|
cbHash = CertHashData->cbHash;
|
|
|
|
if (pEfsUserInfo->pUserCache) {
|
|
|
|
//
|
|
// Check the current against the cache
|
|
//
|
|
|
|
pbWkHash = pEfsUserInfo->pUserCache->pbHash;
|
|
cbCurrentKeyHash = pEfsUserInfo->pUserCache->cbHash;
|
|
|
|
} else {
|
|
|
|
rc = GetCurrentHash(
|
|
pEfsUserInfo,
|
|
&pbCurrentKeyHash,
|
|
&cbCurrentKeyHash
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
pbWkHash = pbCurrentKeyHash;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Compare the hash stored in the current user key
|
|
// with the hash in the specified public key info.
|
|
//
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Compare the thumbprint in the public key info against
|
|
// the user's current.
|
|
//
|
|
|
|
|
|
|
|
if (cbHash == cbCurrentKeyHash) {
|
|
|
|
if (memcmp(pbWkHash, pbHash, cbHash) != 0) {
|
|
|
|
b = FALSE;
|
|
|
|
}
|
|
} else {
|
|
|
|
b = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pbCurrentKeyHash) {
|
|
LsapFreeLsaHeap( pbCurrentKeyHash );
|
|
}
|
|
|
|
if (freeAlignedKey) {
|
|
LsapFreeLsaHeap( pAlignedKey );
|
|
}
|
|
|
|
//
|
|
// If we failed to get the current key hash and reach here, this means we also failed to create the hash.
|
|
// This could mean that we could not set the current key. We could not test if the hash could be the current.
|
|
// We could not replace the DDF with a current one anyway. We assume the passed in hash as the current.
|
|
// Could I be wrong here?
|
|
//
|
|
|
|
return( b );
|
|
}
|
|
|
|
BOOL
|
|
ReplaceUserKey(
|
|
PEFS_USER_INFO pEfsUserInfo,
|
|
PEFS_KEY Fek,
|
|
PEFS_DATA_STREAM_HEADER EfsStream,
|
|
DWORD KeyIndex,
|
|
PEFS_DATA_STREAM_HEADER * UpdatedEfs
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will replace the user key specified by the passed KeyIndex
|
|
with another one that uses the current user EFS keys.
|
|
|
|
It assumes that we are in the context of the caller who owns this key.
|
|
|
|
Arguments:
|
|
|
|
Fek - Supplies the decrypted FEK for the file.
|
|
|
|
EfsStream - Supplies the EFS stream on the file.
|
|
|
|
KeyIndex - Supplies the index of the key to be replaced.
|
|
|
|
UpdatedEfs - Receives a pointer to the new EFS stream to be
|
|
placed on the file.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Query the current user keys. This will give me
|
|
// back a hash and a container and provider name.
|
|
//
|
|
|
|
HCRYPTKEY hKey;
|
|
HCRYPTPROV hProv;
|
|
PBYTE pbHash;
|
|
DWORD cbHash;
|
|
PEFS_KEY_SALT pEfsKeySalt = NULL;
|
|
|
|
BOOL b = FALSE;
|
|
DWORD rc;
|
|
|
|
LPWSTR ContainerName;
|
|
LPWSTR ProviderName;
|
|
LPWSTR DisplayInfo;
|
|
DWORD ProviderType;
|
|
|
|
DebugLog((DEB_WARN, "Updating EFS stream\n" ));
|
|
|
|
PSID NewUserSid = pEfsUserInfo->pTokenUser->User.Sid;
|
|
|
|
rc = GetCurrentKey(
|
|
pEfsUserInfo,
|
|
&hKey,
|
|
&hProv,
|
|
&ContainerName,
|
|
&ProviderName,
|
|
&ProviderType,
|
|
&DisplayInfo,
|
|
&pbHash,
|
|
&cbHash
|
|
);
|
|
|
|
//
|
|
// Use this key information to encrypt the FEK
|
|
// and generate an encrypted key structure.
|
|
//
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
DWORD dwEncryptedFEKLength = 0;
|
|
|
|
PBYTE EncryptedFEK;
|
|
HCRYPTKEY hLocalKey;
|
|
HCRYPTPROV hLocalProv;
|
|
PBYTE pbLocalHash;
|
|
DWORD cbLocalHash;
|
|
LPWSTR lpLocalContainerName;
|
|
LPWSTR lpLocalProviderName;
|
|
LPWSTR lpLocalDisplayInfo;
|
|
|
|
if (pbHash) {
|
|
pbLocalHash = pbHash;
|
|
cbLocalHash = cbHash;
|
|
hLocalKey = hKey;
|
|
hLocalProv = hProv;
|
|
lpLocalContainerName = ContainerName;
|
|
lpLocalProviderName = ProviderName;
|
|
lpLocalDisplayInfo = DisplayInfo;
|
|
} else {
|
|
|
|
ASSERT(pEfsUserInfo->pUserCache);
|
|
|
|
pbLocalHash = pEfsUserInfo->pUserCache->pbHash;
|
|
cbLocalHash = pEfsUserInfo->pUserCache->cbHash;
|
|
hLocalKey = pEfsUserInfo->pUserCache->hUserKey;
|
|
hLocalProv = pEfsUserInfo->pUserCache->hProv;
|
|
lpLocalContainerName = pEfsUserInfo->pUserCache->ContainerName;
|
|
lpLocalProviderName = pEfsUserInfo->pUserCache->ProviderName;
|
|
lpLocalDisplayInfo = pEfsUserInfo->pUserCache->DisplayInformation;
|
|
|
|
}
|
|
|
|
EncryptedFEK = EncryptFEK( Fek, hLocalKey, &dwEncryptedFEKLength );
|
|
|
|
if (EncryptedFEK != NULL) {
|
|
|
|
PEFS_PUBLIC_KEY_INFO PublicKeyInformation = NULL;
|
|
|
|
if (CreatePublicKeyInformationThumbprint(
|
|
NewUserSid,
|
|
pbLocalHash,
|
|
cbLocalHash,
|
|
lpLocalDisplayInfo,
|
|
lpLocalContainerName,
|
|
lpLocalProviderName,
|
|
&PublicKeyInformation
|
|
)) {
|
|
|
|
if ( Fek->Entropy <= EXPORT_KEY_STRENGTH ){
|
|
|
|
DWORD SaltLength;
|
|
DWORD SaltBlockLength;
|
|
|
|
if (GetSaltLength(Fek->Algorithm, &SaltLength, &SaltBlockLength)){
|
|
|
|
pEfsKeySalt = (PEFS_KEY_SALT)LsapAllocateLsaHeap( sizeof( EFS_KEY_SALT ) + SaltBlockLength );
|
|
|
|
if (pEfsKeySalt){
|
|
pEfsKeySalt->Length = sizeof( EFS_KEY_SALT ) + SaltBlockLength;
|
|
pEfsKeySalt->SaltType = Fek->Algorithm;
|
|
RtlCopyMemory( (PBYTE)pEfsKeySalt + sizeof( EFS_KEY_SALT ),
|
|
EFS_KEY_DATA( Fek ),
|
|
SaltLength
|
|
);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
pEfsKeySalt = NULL;
|
|
}
|
|
|
|
if (pEfsKeySalt || (Fek->Entropy > EXPORT_KEY_STRENGTH)) {
|
|
|
|
//
|
|
// This should return an error
|
|
//
|
|
|
|
DWORD EncryptedKeySize = 0;
|
|
PENCRYPTED_KEY EncryptedKey;
|
|
|
|
rc = ConstructEncryptedKey( EncryptedFEK,
|
|
dwEncryptedFEKLength,
|
|
PublicKeyInformation,
|
|
pEfsKeySalt,
|
|
&EncryptedKey,
|
|
&EncryptedKeySize
|
|
);
|
|
//
|
|
// We'll check the return code below
|
|
//
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
|
|
PEFS_DATA_STREAM_HEADER NewEfs = NULL;
|
|
|
|
if (AppendEncryptedKeyToDDF(
|
|
EfsStream,
|
|
EncryptedKey,
|
|
Fek,
|
|
&NewEfs
|
|
)) {
|
|
|
|
PEFS_DATA_STREAM_HEADER pNewEfs2 = NULL;
|
|
|
|
if (DeleteEncryptedKeyByIndex(
|
|
NewEfs,
|
|
KeyIndex,
|
|
Fek,
|
|
&pNewEfs2
|
|
)) {
|
|
|
|
*UpdatedEfs = pNewEfs2;
|
|
b = TRUE;
|
|
|
|
} else {
|
|
|
|
*UpdatedEfs = NULL; // paranoia
|
|
}
|
|
|
|
LsapFreeLsaHeap( NewEfs );
|
|
}
|
|
|
|
LsapFreeLsaHeap( EncryptedKey );
|
|
|
|
}
|
|
|
|
if (pEfsKeySalt){
|
|
LsapFreeLsaHeap( pEfsKeySalt );
|
|
}
|
|
}
|
|
|
|
LsapFreeLsaHeap( PublicKeyInformation );
|
|
}
|
|
|
|
LsapFreeLsaHeap( EncryptedFEK );
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
|
|
}
|
|
|
|
if (ContainerName) {
|
|
LsapFreeLsaHeap( ContainerName );
|
|
}
|
|
|
|
if (ProviderName) {
|
|
LsapFreeLsaHeap( ProviderName );
|
|
}
|
|
|
|
if (DisplayInfo) {
|
|
LsapFreeLsaHeap( DisplayInfo );
|
|
}
|
|
|
|
if (pbHash) {
|
|
LsapFreeLsaHeap( pbHash );
|
|
}
|
|
|
|
if (hKey) {
|
|
CryptDestroyKey( hKey );
|
|
}
|
|
|
|
if (hProv) {
|
|
CryptReleaseContext( hProv, 0 );
|
|
}
|
|
|
|
}
|
|
|
|
SetLastError( rc );
|
|
|
|
if (!b) {
|
|
DebugLog((DEB_ERROR, "Update failed, error = %x\n" ,GetLastError() ));
|
|
}
|
|
|
|
return( b );
|
|
}
|
|
|
|
BOOL
|
|
DeleteEncryptedKeyByIndex(
|
|
IN PEFS_DATA_STREAM_HEADER pEfs,
|
|
IN DWORD KeyIndex,
|
|
IN PEFS_KEY Fek,
|
|
OUT PEFS_DATA_STREAM_HEADER * pNewEfs
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine deletes the passed key from the DDF of the passed
|
|
EFS stream, and returns a new EFS stream. It does not deallocate
|
|
the original EFS stream.
|
|
|
|
Arguments:
|
|
|
|
pEfs - Supplies a pointer to the original EFS stream.
|
|
|
|
KeyIndex - Supplies the index of the key to delete.
|
|
|
|
pNewEfs - Returns a pointer to the new EFS stream allocated
|
|
out of heap.
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure. GetLastError() will return more information.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL b = FALSE;
|
|
|
|
//
|
|
// Do this the lazy way: build the new DDF
|
|
// and copy it into the EFS stream.
|
|
//
|
|
|
|
PENCRYPTED_KEYS pDDF = (PENCRYPTED_KEYS)OFFSET_TO_POINTER( DataDecryptionField, pEfs );
|
|
|
|
//
|
|
// This is an over estimate, but it will do.
|
|
//
|
|
|
|
PENCRYPTED_KEYS pNewDDF = (PENCRYPTED_KEYS)LsapAllocateLsaHeap( GetLengthEncryptedKeys( pDDF ) );
|
|
|
|
if (pNewDDF) {
|
|
|
|
pNewDDF->KeyCount = pDDF->KeyCount - 1;
|
|
DWORD cbNewDDF = sizeof( ENCRYPTED_KEYS ) - sizeof( ENCRYPTED_KEY );
|
|
|
|
PBYTE Target = (PBYTE)(&pNewDDF->EncryptedKey[0]);
|
|
PBYTE Source = (PBYTE)(&pDDF->EncryptedKey[0]);
|
|
|
|
for (DWORD i=0; i<pDDF->KeyCount ; i++) {
|
|
|
|
if (i != KeyIndex) {
|
|
|
|
//
|
|
// We want this one. Copy it.
|
|
//
|
|
|
|
DWORD KeyLength = *((DWORD UNALIGNED*) &((PENCRYPTED_KEY)Source)->Length);
|
|
cbNewDDF += KeyLength;
|
|
|
|
memcpy( Target, Source, KeyLength );
|
|
|
|
Target = (PBYTE)NEXT_ENCRYPTED_KEY( Target );
|
|
Source = (PBYTE)NEXT_ENCRYPTED_KEY( Source );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Skip this one.
|
|
//
|
|
|
|
Source = (PBYTE)NEXT_ENCRYPTED_KEY( Source );
|
|
}
|
|
}
|
|
|
|
//
|
|
// pNewDDF contains a pointer to our new DDF.
|
|
//
|
|
|
|
PENCRYPTED_KEYS pDRF = (PENCRYPTED_KEYS)OFFSET_TO_POINTER( DataRecoveryField, pEfs );
|
|
DWORD cbDRF;
|
|
|
|
if ((PVOID)pDRF == (PVOID)pEfs) {
|
|
|
|
//
|
|
// There was no DRF
|
|
//
|
|
|
|
cbDRF = 0;
|
|
pDRF = NULL;
|
|
|
|
} else {
|
|
cbDRF = GetLengthEncryptedKeys( pDRF );
|
|
}
|
|
|
|
*pNewEfs = AssembleEfsStream( pNewDDF, cbNewDDF, pDRF, cbDRF, Fek );
|
|
|
|
if (*pNewEfs) {
|
|
b = TRUE;
|
|
} else {
|
|
*pNewEfs = NULL; // paranoia
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
}
|
|
|
|
LsapFreeLsaHeap( pNewDDF );
|
|
|
|
} else {
|
|
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
}
|
|
|
|
return( b );
|
|
}
|
|
|
|
|
|
PEFS_DATA_STREAM_HEADER
|
|
AssembleEfsStream(
|
|
IN PENCRYPTED_KEYS pDDF,
|
|
IN DWORD cbDDF,
|
|
IN PENCRYPTED_KEYS pDRF,
|
|
IN DWORD cbDRF,
|
|
IN PEFS_KEY Fek
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes the pieces of an EFS stream and assembles them into
|
|
an EFS stream.
|
|
|
|
Arguments:
|
|
|
|
pDDF - Supplies a pointer to the DDF for the new EFS stream.
|
|
|
|
cbDDF - Supplies the length in bytes of the DDF.
|
|
|
|
pDRF - Supplies a pointer to the DRF for the new EFS stream.
|
|
|
|
cbDRF - Supplies the length in bytes of the DRF.
|
|
|
|
Return Value:
|
|
|
|
Returns a pointer to a new EFS stream, or NULL. Caller is responsible
|
|
for freeing the returned memory.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Compute the total size of the new EFS stream
|
|
//
|
|
|
|
DWORD cbNewEFS = sizeof( EFS_DATA_STREAM_HEADER ) + cbDDF + cbDRF;
|
|
cbNewEFS = (cbNewEFS + 7) & 0xfffffff8;
|
|
|
|
PEFS_DATA_STREAM_HEADER pNewEFS = (PEFS_DATA_STREAM_HEADER)LsapAllocateLsaHeap( cbNewEFS );
|
|
|
|
if (pNewEFS) {
|
|
|
|
memset( pNewEFS, 0, sizeof( EFS_DATA_STREAM_HEADER ) );
|
|
|
|
pNewEFS->Length = cbNewEFS;
|
|
pNewEFS->State = 0;
|
|
pNewEFS->EfsVersion = EFS_CURRENT_VERSION;
|
|
|
|
RPC_STATUS RpcStatus = UuidCreate( &pNewEFS->EfsId );
|
|
|
|
if (RpcStatus == ERROR_SUCCESS || RpcStatus == RPC_S_UUID_LOCAL_ONLY) {
|
|
|
|
//
|
|
// Copy in the DDF
|
|
//
|
|
|
|
PBYTE Base = (PBYTE)(((PBYTE)pNewEFS) + sizeof( EFS_DATA_STREAM_HEADER ));
|
|
pNewEFS->DataDecryptionField = (ULONG)POINTER_TO_OFFSET( Base, pNewEFS );
|
|
memcpy( Base, pDDF, cbDDF );
|
|
|
|
Base += cbDDF;
|
|
|
|
//
|
|
// Copy the DRF
|
|
//
|
|
|
|
if (pDRF) {
|
|
memcpy( Base, pDRF, cbDRF );
|
|
pNewEFS->DataRecoveryField = (ULONG)POINTER_TO_OFFSET( Base, pNewEFS );
|
|
} else {
|
|
pNewEFS->DataRecoveryField = 0;
|
|
}
|
|
|
|
|
|
// Base += cbDRF
|
|
|
|
// EfspChecksumEfs( pNewEFS, Fek );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Couldn't get a UUID, fail
|
|
//
|
|
|
|
LsapFreeLsaHeap( pNewEFS );
|
|
pNewEFS = NULL;
|
|
}
|
|
}
|
|
|
|
return( pNewEFS );
|
|
}
|
|
|
|
BOOL
|
|
RecoveryInformationCurrent(
|
|
PEFS_DATA_STREAM_HEADER EfsStream
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine examines the recovery information in an EFS stream and determines if
|
|
the system recovery information has changed since this stream was generated. It
|
|
does this by comparing the certificate hashes stored in the current recovery
|
|
information with the certificate hashes stored in the passed DRF.
|
|
|
|
Arguments:
|
|
|
|
EfsStream - Supplies a pointer to the EFS stream to be examined.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Recovery information is up to date.
|
|
|
|
FALSE - DRF must be regenerated with new recovery information.
|
|
|
|
--*/
|
|
{
|
|
|
|
//
|
|
// Assume that the entries in the DRF correspond to entries in the
|
|
// current recovery information array in order. That will simplify
|
|
// this operation considerably.
|
|
//
|
|
|
|
//
|
|
// Get a pointer to the DRF
|
|
//
|
|
|
|
PDRF pDrf = (PENCRYPTED_KEYS)OFFSET_TO_POINTER( DataRecoveryField, EfsStream );
|
|
|
|
//
|
|
// For each entry in the DRF, compare hash to corresponding entry in recovery
|
|
// policy. Fail on first mismatch.
|
|
//
|
|
|
|
ULONG KeyCount = 0;
|
|
|
|
if ((PVOID)pDrf == (PVOID)EfsStream) {
|
|
|
|
//
|
|
// No DRF field.
|
|
//
|
|
|
|
pDrf = NULL;
|
|
|
|
}
|
|
|
|
//
|
|
// pDrf->KeyCount could be unaligned
|
|
//
|
|
|
|
if (pDrf) {
|
|
RtlCopyMemory(&KeyCount, &pDrf->KeyCount, sizeof(ULONG));
|
|
}
|
|
|
|
/*
|
|
//
|
|
// There may not be a recovery policy on this machine. If that's the case,
|
|
// we're just going to leave this file alone.
|
|
//
|
|
|
|
if (CurrentRecoveryPolicy.dwKeyCount == 0) {
|
|
return( TRUE );
|
|
}
|
|
*/
|
|
|
|
if (!pDrf && CurrentRecoveryPolicy.PolicyStatus < RECOVERY_POLICY_OK) {
|
|
|
|
//
|
|
// Current recovery policy has no valid recovery agent and the existing $EFS
|
|
// has no valid agent too.
|
|
//
|
|
|
|
return (TRUE);
|
|
|
|
}
|
|
|
|
if (CurrentRecoveryPolicy.dwKeyCount != KeyCount) {
|
|
return( FALSE );
|
|
}
|
|
|
|
ASSERT(pDrf);
|
|
if (!pDrf) {
|
|
|
|
//
|
|
// We should never get into this. This is only for the purpose of defensive.
|
|
//
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
PENCRYPTED_KEY pEncryptedKey = &pDrf->EncryptedKey[0];
|
|
|
|
for (ULONG i=0; i<KeyCount ; i++) {
|
|
|
|
PENCRYPTED_KEY pAlignedKey;
|
|
BOOLEAN freeAlignedKey;
|
|
|
|
(VOID) EfsAlignBlock(
|
|
pEncryptedKey,
|
|
(PVOID *)&pAlignedKey,
|
|
&freeAlignedKey
|
|
);
|
|
if (!pAlignedKey) {
|
|
|
|
//
|
|
// OOM. Treat it as not current.
|
|
//
|
|
|
|
return (FALSE);
|
|
|
|
}
|
|
|
|
PEFS_PUBLIC_KEY_INFO PublicKeyInfo = (PEFS_PUBLIC_KEY_INFO)OFFSET_TO_POINTER( PublicKeyInfo, pAlignedKey );
|
|
ASSERT( PublicKeyInfo->KeySourceTag == EfsCertificateThumbprint );
|
|
|
|
PEFS_CERT_HASH_DATA CertHashData = (PEFS_CERT_HASH_DATA)OFFSET_TO_POINTER( CertificateThumbprint.CertHashData, PublicKeyInfo );
|
|
PBYTE pbHash = (PBYTE)OFFSET_TO_POINTER( pbHash, CertHashData );
|
|
DWORD cbHash = CertHashData->cbHash;
|
|
|
|
if ((cbHash != CurrentRecoveryPolicy.cbHash[i]) || (memcmp(pbHash, CurrentRecoveryPolicy.pbHash[i], cbHash) != 0)) {
|
|
|
|
if (freeAlignedKey) {
|
|
LsapFreeLsaHeap( pAlignedKey );
|
|
}
|
|
return( FALSE );
|
|
|
|
} else {
|
|
|
|
pEncryptedKey = (PENCRYPTED_KEY)(((PBYTE)(pEncryptedKey)) + pAlignedKey->Length);
|
|
if (freeAlignedKey) {
|
|
LsapFreeLsaHeap( pAlignedKey );
|
|
}
|
|
}
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
DWORD
|
|
UpdateRecoveryInformation(
|
|
PEFS_KEY Fek,
|
|
PEFS_DATA_STREAM_HEADER EfsStream,
|
|
PEFS_DATA_STREAM_HEADER * UpdatedEfs
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will create a new EFS stream based on the passed in one.
|
|
The new EFS stream will contain a DRF based on the current recovery
|
|
policy. It is assumed that someone else has already verified that
|
|
the DRF is this stream is out of date, this routine will not do that.
|
|
|
|
Arguments:
|
|
|
|
Fek - The FEK for the file being updated.
|
|
|
|
EfsStream - Supplies the existing EFS stream for the file.
|
|
|
|
UpdatedEfs - Returns an updated EFS stream for the file, allocated out of heap.
|
|
|
|
|
|
Return Value:
|
|
|
|
return-value - Description of conditions needed to return value. - or -
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
DWORD rc;
|
|
DWORD cbDDF = 0;
|
|
DWORD cbDRF = 0;
|
|
PENCRYPTED_KEYS pNewDRF;
|
|
|
|
*UpdatedEfs = NULL;
|
|
|
|
|
|
//
|
|
// Simply generate a new DRF and stick it onto the end of the existing EFS
|
|
// stream.
|
|
//
|
|
|
|
PDDF pDdf = (PENCRYPTED_KEYS)OFFSET_TO_POINTER( DataDecryptionField, EfsStream );
|
|
|
|
cbDDF = GetLengthEncryptedKeys( pDdf );
|
|
|
|
rc = GenerateDRF( Fek, &pNewDRF, &cbDRF);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
*UpdatedEfs = AssembleEfsStream( pDdf, cbDDF, pNewDRF, cbDRF, Fek );
|
|
|
|
if (*UpdatedEfs == NULL) {
|
|
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
LsapFreeLsaHeap( pNewDRF );
|
|
|
|
}
|
|
|
|
ReleaseRecoveryData();
|
|
|
|
return( rc );
|
|
}
|
|
|
|
|
|
DWORD
|
|
DecryptFek(
|
|
IN PEFS_USER_INFO pEfsUserInfo,
|
|
IN PEFS_DATA_STREAM_HEADER EfsStream,
|
|
OUT PEFS_KEY * Fek,
|
|
OUT PEFS_DATA_STREAM_HEADER * NewEfs,
|
|
IN ULONG OpenType
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will extract the FEK from the passed Efs stream.
|
|
It will also check to see if the EFS stream is up to date w.r.t.
|
|
current keys and recovery policy, and if not, it will generate
|
|
a new one.
|
|
|
|
Arguments:
|
|
|
|
|
|
EfsStream - Supplies the EFS stream from the file being opened.
|
|
|
|
Fek - Returns the decrypted FEK from the EFS stream. This data is
|
|
allocated out of local heap and must be freed by the caller.
|
|
|
|
NewEfs - Returns a new EFS stream for the file if necessary, otherwise
|
|
returns NULL. This data is allocated out of local heap and must be
|
|
freed by the caller
|
|
|
|
OpenType - Whether this is a normal open or an open for recovery.
|
|
|
|
Return Value:
|
|
|
|
return-value - Description of conditions needed to return value. - or -
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
DWORD rc = ERROR_SUCCESS;
|
|
DWORD KeyIndex;
|
|
PEFS_DATA_STREAM_HEADER UpdatedEfs = NULL;
|
|
BOOLEAN Recovery = FALSE;
|
|
BOOLEAN bEfsInvalid = FALSE;
|
|
BOOLEAN DRFIsCurrent = FALSE;
|
|
BOOLEAN ReqUpdateDRF = FALSE;
|
|
|
|
*Fek = NULL;
|
|
*NewEfs = NULL;
|
|
|
|
if (EfsStream->EfsVersion > EFS_CURRENT_VERSION) {
|
|
return ERROR_EFS_VERSION_NOT_SUPPORT;
|
|
}
|
|
|
|
__try {
|
|
|
|
#if DBG
|
|
|
|
UUID * EfsUuid = &EfsStream->EfsId;
|
|
|
|
WCHAR * StringUuid;
|
|
|
|
if (STATUS_SUCCESS == UuidToString ( EfsUuid, &StringUuid )) {
|
|
|
|
DebugLog((DEB_TRACE_EFS, "Found $EFS w/ id: %ws\n" ,StringUuid ));
|
|
|
|
RpcStringFree( &StringUuid) ;
|
|
}
|
|
|
|
#endif
|
|
|
|
//
|
|
// First try the DDF, and if we strike out there, the DRF.
|
|
//
|
|
|
|
PDDF Ddf = (PENCRYPTED_KEYS)OFFSET_TO_POINTER( DataDecryptionField, EfsStream );
|
|
PDRF Drf = (PENCRYPTED_KEYS)OFFSET_TO_POINTER( DataRecoveryField, EfsStream );
|
|
|
|
if ((PVOID)Drf == (PVOID)EfsStream) {
|
|
|
|
//
|
|
// No DRF field.
|
|
//
|
|
|
|
Drf = NULL;
|
|
|
|
}
|
|
|
|
*Fek = GetFekFromEncryptedKeys( pEfsUserInfo, (PENCRYPTED_KEYS)Ddf, TRUE, &KeyIndex );
|
|
|
|
if ((NULL == *Fek) && Drf) {
|
|
|
|
*Fek = GetFekFromEncryptedKeys( pEfsUserInfo, (PENCRYPTED_KEYS)Drf, TRUE, &KeyIndex );
|
|
Recovery = TRUE;
|
|
}
|
|
|
|
if (*Fek == NULL) {
|
|
|
|
//
|
|
// Bad keyset means that none of the keysets in the current
|
|
// context could decrypt this file.
|
|
//
|
|
|
|
if (GetLastError() == NTE_BAD_KEYSET) {
|
|
|
|
return ( ERROR_DECRYPTION_FAILED );
|
|
|
|
} else {
|
|
|
|
return ( GetLastError() );
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// If we opened the file via the DDF, make sure
|
|
// that the entry we used is current for the current
|
|
// user.
|
|
//
|
|
|
|
if (!Recovery) {
|
|
|
|
if (!UserKeyCurrent( pEfsUserInfo, Ddf, KeyIndex )) {
|
|
|
|
//
|
|
// The index we used to open the file
|
|
// is not current. Replace with the current user
|
|
// key.
|
|
//
|
|
|
|
(VOID) ReplaceUserKey( pEfsUserInfo, *Fek, EfsStream, KeyIndex, &UpdatedEfs );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Checksum the EFS stream to make sure it has not been tampered with.
|
|
// If it is changed, we will try to see if the DRF has been checnged or not.
|
|
//
|
|
/*
|
|
if (!EfspValidateEfsStream( EfsStream, *Fek )) {
|
|
|
|
//
|
|
// Checksum not match. See if the DRF changed
|
|
//
|
|
|
|
PENCRYPTED_KEYS pNewDRF;
|
|
DWORD cbDRF;
|
|
|
|
rc = GenerateDRF(*Fek, &pNewDRF, &cbDRF);
|
|
if ( ERROR_SUCCESS == rc ) {
|
|
|
|
//
|
|
// Let's see if the DRF matches
|
|
//
|
|
|
|
if (EqualEncryptedKeys(Drf, pNewDRF, cbDRF)) {
|
|
|
|
//
|
|
// DRF is not modified. We can't fix the modification
|
|
// Regenerate the check sum.
|
|
//
|
|
|
|
DRFIsCurrent = TRUE;
|
|
|
|
if (!UpdatedEfs) {
|
|
|
|
//
|
|
// If $EFS is updated above, we don't need to generate the check sum again.
|
|
//
|
|
|
|
UpdatedEfs = (PEFS_DATA_STREAM_HEADER)LsapAllocateLsaHeap( EfsStream->Length );
|
|
if (UpdatedEfs) {
|
|
|
|
RtlCopyMemory(UpdatedEfs, EfsStream, EfsStream->Length);
|
|
memset( &UpdatedEfs->EfsHash, 0, MD5_HASH_SIZE );
|
|
EfspChecksumEfs( UpdatedEfs, *Fek );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// This is the best effort. If we failed to get the memory in the above
|
|
// We will still try to let user open the file. But not fix the check sum
|
|
//
|
|
|
|
|
|
|
|
} else {
|
|
|
|
//
|
|
// Either the checksum is modified, or the DRF is modified.
|
|
// Do the check sum with the new DRF
|
|
//
|
|
|
|
ReqUpdateDRF = TRUE;
|
|
|
|
}
|
|
|
|
LsapFreeLsaHeap( pNewDRF );
|
|
|
|
}
|
|
|
|
ReleaseRecoveryData();
|
|
|
|
}
|
|
*/
|
|
|
|
|
|
//
|
|
// Regardless of whether we did a recovery or not,
|
|
// we still need to check to see if the recovery
|
|
// information is up to snuff.
|
|
//
|
|
|
|
if ( !RecoveryInformationCurrent( EfsStream ) ) {
|
|
|
|
//
|
|
// We may have fixed up the current user key
|
|
// above. If so, modify that EFS stream.
|
|
// Otherwise, use the one that the user
|
|
// passed in.
|
|
//
|
|
|
|
if (UpdatedEfs) {
|
|
|
|
PEFS_DATA_STREAM_HEADER Tmp;
|
|
rc = UpdateRecoveryInformation( *Fek, UpdatedEfs, &Tmp );
|
|
|
|
if (ERROR_SUCCESS == rc) {
|
|
LsapFreeLsaHeap( UpdatedEfs );
|
|
UpdatedEfs = Tmp;
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = UpdateRecoveryInformation( *Fek, EfsStream, &UpdatedEfs );
|
|
}
|
|
}
|
|
|
|
//
|
|
// We successfully decrypted the file, but we may
|
|
// not have been able to update the various parts
|
|
// That's ok, we'll let the file decrypt.
|
|
//
|
|
// Note that, if there have been no updates,
|
|
// UpdatedEfs is NULL, so this is a safe thing to do.
|
|
//
|
|
|
|
*NewEfs = UpdatedEfs;
|
|
|
|
return ( ERROR_SUCCESS );
|
|
}
|
|
|
|
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
rc = GetExceptionCode();
|
|
|
|
//
|
|
// Clean up
|
|
//
|
|
|
|
if (UpdatedEfs) {
|
|
LsapFreeLsaHeap( UpdatedEfs );
|
|
}
|
|
|
|
if (*Fek != NULL) {
|
|
LsapFreeLsaHeap( *Fek );
|
|
*Fek = NULL;
|
|
}
|
|
}
|
|
|
|
return( rc );
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
EfsGetFek(
|
|
IN PEFS_USER_INFO pEfsUserInfo,
|
|
IN PEFS_DATA_STREAM_HEADER EfsStream,
|
|
OUT PEFS_KEY * Fek
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will extract the FEK from the passed Efs stream.
|
|
|
|
Arguments:
|
|
|
|
|
|
EfsStream - Supplies the EFS stream from the file being opened.
|
|
|
|
Fek - Returns the decrypted FEK from the EFS stream. This data is
|
|
allocated out of local heap and must be freed by the caller.
|
|
|
|
|
|
Return Value:
|
|
|
|
return-value - Description of conditions needed to return value. - or -
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
DWORD rc = ERROR_SUCCESS;
|
|
DWORD KeyIndex;
|
|
|
|
*Fek = NULL;
|
|
|
|
__try {
|
|
|
|
|
|
//
|
|
// First try the DDF, and if we strike out there, the DRF.
|
|
//
|
|
|
|
PDDF Ddf = (PENCRYPTED_KEYS)OFFSET_TO_POINTER( DataDecryptionField, EfsStream );
|
|
PDRF Drf = (PENCRYPTED_KEYS)OFFSET_TO_POINTER( DataRecoveryField, EfsStream );
|
|
|
|
*Fek = GetFekFromEncryptedKeys( pEfsUserInfo, (PENCRYPTED_KEYS)Ddf, FALSE, &KeyIndex );
|
|
|
|
if ((NULL == *Fek) && ( (PVOID)Drf != (PVOID)EfsStream) ) {
|
|
|
|
*Fek = GetFekFromEncryptedKeys( pEfsUserInfo, (PENCRYPTED_KEYS)Drf, FALSE, &KeyIndex );
|
|
|
|
}
|
|
|
|
if (*Fek == NULL) {
|
|
|
|
return (rc = GetLastError() );
|
|
|
|
}
|
|
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
rc = GetExceptionCode();
|
|
|
|
if (*Fek != NULL) {
|
|
LsapFreeLsaHeap( *Fek );
|
|
*Fek = NULL;
|
|
}
|
|
}
|
|
|
|
return( rc );
|
|
|
|
}
|
|
|
|
|
|
PEFS_KEY
|
|
ExtractFek(
|
|
IN PEFS_USER_INFO pEfsUserInfo,
|
|
IN PENCRYPTED_KEY EncryptedKey,
|
|
IN BOOL CheckBits
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will take the passed EncryptedKey structure and attempt
|
|
to decrypt the FEK encoded in the structure. It will do this by first
|
|
attempting to create context using the Provider and Container names
|
|
contained in the structure. If such a context exists, its key
|
|
exchange key is used to import the encrypted session key from the
|
|
structure.
|
|
|
|
Once the session key has been imported, it is used to decrypt the encrypted
|
|
FEK structure. All of the pieces are then used to reconstruct the key
|
|
integrity information structure, and if this verifies, then it is assumed
|
|
that the FEK has been decoded correctly.
|
|
|
|
Arguments:
|
|
|
|
pEfsUserInfo - User Info.
|
|
|
|
EncryptedKey - Supplies a pointer to an EncryptedKey from the file being opened.
|
|
|
|
CheckBits - TRUE will check the encryption bits against current version.
|
|
|
|
Return Value:
|
|
|
|
On success, returns a pointer to a decrypted FEK. On failure, returns NULL.
|
|
|
|
The returned pointer is allocated out of heap and must be freed.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Obtain a context to the user's RSA key
|
|
//
|
|
|
|
DWORD LastError = ERROR_SUCCESS;
|
|
PEFS_KEY DecryptedFEK = NULL;
|
|
PEFS_PUBLIC_KEY_INFO PublicKeyInfo = (PEFS_PUBLIC_KEY_INFO) OFFSET_TO_POINTER(PublicKeyInfo, EncryptedKey);
|
|
HCRYPTPROV hProv = 0;
|
|
HCRYPTKEY hKey;
|
|
|
|
HCRYPTPROV hWkProv;
|
|
HCRYPTKEY hWkKey;
|
|
|
|
switch (PublicKeyInfo->KeySourceTag) {
|
|
case EfsCertificateThumbprint:
|
|
{
|
|
//
|
|
// See if there is a cert in the current context
|
|
// that corresponds to this thumbprint. If so,
|
|
// we're in business.
|
|
// The KeySourceTag has been changed a couple of times
|
|
// during the development. Now this is the only valid tag.
|
|
//
|
|
|
|
PBYTE pbHash;
|
|
DWORD cbHash;
|
|
|
|
PEFS_CERT_HASH_DATA CertHashData = (PEFS_CERT_HASH_DATA)OFFSET_TO_POINTER( CertificateThumbprint.CertHashData, PublicKeyInfo );
|
|
pbHash = (PBYTE)OFFSET_TO_POINTER( pbHash, CertHashData );
|
|
cbHash = CertHashData->cbHash;
|
|
|
|
LastError = GetKeyInfoFromCertHash(
|
|
pEfsUserInfo,
|
|
pbHash,
|
|
cbHash,
|
|
&hKey,
|
|
&hProv,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if (LastError != ERROR_SUCCESS) {
|
|
SetLastError( LastError );
|
|
return( NULL );
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return( NULL );
|
|
break;
|
|
}
|
|
|
|
if (hKey) {
|
|
|
|
//
|
|
// We are not using the cache.
|
|
//
|
|
|
|
hWkKey = hKey;
|
|
|
|
} else {
|
|
|
|
ASSERT(pEfsUserInfo->pUserCache);
|
|
|
|
hWkKey = pEfsUserInfo->pUserCache->hUserKey;
|
|
|
|
}
|
|
|
|
//
|
|
// Decrypt the FEK field with the session key
|
|
//
|
|
|
|
PBYTE EncryptedFEK = (PBYTE)OFFSET_TO_POINTER( EncryptedFEK, EncryptedKey );
|
|
|
|
//
|
|
// Copy the FEK into a temporary buffer for decryption
|
|
//
|
|
|
|
DWORD cbData = EncryptedKey->EncryptedFEKLength;
|
|
|
|
DecryptedFEK = (PEFS_KEY)LsapAllocateLsaHeap( cbData );
|
|
|
|
if (DecryptedFEK != NULL) {
|
|
|
|
memcpy( DecryptedFEK, EncryptedFEK, cbData );
|
|
|
|
BOOL Verified = FALSE;
|
|
|
|
if (CryptDecrypt( hWkKey, 0, TRUE, 0, (PBYTE)DecryptedFEK, &cbData )) {
|
|
|
|
//
|
|
// First, perform a sanity check: make sure the key we just decrypted has a length field
|
|
// that's reasonable. If not, we got back garbage.
|
|
//
|
|
|
|
if (EFS_KEY_SIZE( DecryptedFEK) <= cbData) {
|
|
|
|
//
|
|
// Check the Entropy and the salt here
|
|
//
|
|
|
|
PEFS_KEY_SALT pEfsKeySalt = (PEFS_KEY_SALT)OFFSET_TO_POINTER( EfsKeySalt, EncryptedKey );
|
|
|
|
if ( (KeyEntropy == EXPORT_KEY_STRENGTH) && CheckBits){
|
|
if ( DecryptedFEK->Entropy <= KeyEntropy ){
|
|
|
|
//
|
|
// Check the salt
|
|
//
|
|
|
|
DWORD SaltLength;
|
|
DWORD SaltBlockLength;
|
|
|
|
if (GetSaltLength(DecryptedFEK->Algorithm, &SaltLength,&SaltBlockLength)){
|
|
if ( pEfsKeySalt ){
|
|
Verified = (memcmp( EFS_KEY_DATA(DecryptedFEK), (PBYTE)pEfsKeySalt + sizeof(EFS_KEY_SALT), SaltLength ) == 0);
|
|
} else {
|
|
|
|
//
|
|
// This should not happen
|
|
//
|
|
|
|
ASSERT(FALSE);
|
|
Verified = FALSE;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// This algorithm has no salt
|
|
//
|
|
|
|
Verified = TRUE;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Export version cannot decrypt files encrypted with longer keys
|
|
//
|
|
|
|
Verified = FALSE;
|
|
}
|
|
|
|
} else {
|
|
|
|
Verified = TRUE;
|
|
}
|
|
|
|
//robertg Now you have a pointer to the salt structure to play with. Set Verified == TRUE if
|
|
// everything checks out.
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// If we got back a bad length error, that means that the plaintext of
|
|
// FEK was larger than the cyphertext. Assume that this can't happen,
|
|
// since the CryptDecrypt interface doesn't seem to be able to handle
|
|
// this situation.
|
|
//
|
|
|
|
ASSERT(GetLastError() != NTE_BAD_LEN);
|
|
|
|
LastError = GetLastError();
|
|
}
|
|
|
|
if (!Verified) {
|
|
|
|
LsapFreeLsaHeap( DecryptedFEK );
|
|
DecryptedFEK = NULL;
|
|
|
|
LastError = ERROR_DECRYPTION_FAILED;
|
|
}
|
|
|
|
} else {
|
|
|
|
LastError = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
//
|
|
// Clean up what we allocated.
|
|
//
|
|
|
|
if (hKey) {
|
|
|
|
CryptDestroyKey( hKey );
|
|
CryptReleaseContext( hProv, 0 );
|
|
|
|
}
|
|
|
|
|
|
SetLastError( LastError );
|
|
|
|
return( DecryptedFEK );
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
GenerateFEK(
|
|
IN OUT PEFS_KEY *Key
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generates a new File Encryption Key (FEK).
|
|
|
|
Arguments:
|
|
|
|
Key - Supplies a pointer to PEFS_KEY.
|
|
|
|
Return Value:
|
|
|
|
Error if not enough space can be located.
|
|
|
|
|
|
--*/
|
|
{
|
|
PBYTE KeyData;
|
|
ULONG KeyLength;
|
|
BOOL b = FALSE;
|
|
DWORD LiveKeyEntropy;
|
|
|
|
//
|
|
// Allocate the buffer for the EFS_KEY.
|
|
// Set the algorithm and key length here.
|
|
//
|
|
|
|
switch (EfsAlgInForce) {
|
|
|
|
case CALG_3DES:
|
|
|
|
//
|
|
// DES3 has no international version
|
|
//
|
|
|
|
KeyLength = DES3_KEYSIZE;
|
|
LiveKeyEntropy = DES3_KEY_STRENGTH;
|
|
break;
|
|
|
|
case CALG_DESX:
|
|
KeyLength = DESX_KEYSIZE - 8;
|
|
LiveKeyEntropy = KeyEntropy;
|
|
break;
|
|
|
|
case CALG_AES_256:
|
|
default:
|
|
|
|
KeyLength = AES_KEYSIZE_256;
|
|
LiveKeyEntropy = AES_KEY_STRENGTH_256;
|
|
break;
|
|
|
|
}
|
|
|
|
*Key = (PEFS_KEY)LsapAllocateLsaHeap( sizeof( EFS_KEY ) + KeyLength );
|
|
if ( NULL == *Key ){
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
(*Key)->KeyLength = KeyLength;
|
|
(*Key)->Algorithm = EfsAlgInForce;
|
|
|
|
|
|
KeyData = (PBYTE)(((PBYTE)*Key) + sizeof( EFS_KEY ));
|
|
|
|
if (b = CryptGenRandom( hProvVerify, (*Key)->KeyLength, KeyData )) {
|
|
(*Key)->Entropy = LiveKeyEntropy;
|
|
} else {
|
|
LsapFreeLsaHeap( *Key );
|
|
*Key = NULL;
|
|
}
|
|
|
|
|
|
return( b != 0);
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
CreatePublicKeyInformationCertificate(
|
|
IN PSID pUserSid OPTIONAL,
|
|
PBYTE pbCert,
|
|
DWORD cbCert,
|
|
OUT PEFS_PUBLIC_KEY_INFO * PublicKeyInformation
|
|
)
|
|
{
|
|
DWORD PublicKeyInformationLength = 0;
|
|
DWORD UserSidLength = 0;
|
|
PWCHAR Base;
|
|
|
|
if (pUserSid != NULL) {
|
|
UserSidLength = GetLengthSid( pUserSid );
|
|
}
|
|
|
|
//
|
|
// Total size is the size of the public key info structure, the size of the
|
|
// cert hash data structure, the length of the thumbprint, and the lengths of the
|
|
// container name and provider name if they were passed.
|
|
//
|
|
|
|
PublicKeyInformationLength = sizeof( EFS_PUBLIC_KEY_INFO ) + UserSidLength + cbCert;
|
|
|
|
//
|
|
// Allocate and fill in the PublicKeyInformation structure
|
|
//
|
|
|
|
*PublicKeyInformation = (PEFS_PUBLIC_KEY_INFO)LsapAllocateLsaHeap( PublicKeyInformationLength );
|
|
|
|
if (*PublicKeyInformation == NULL) {
|
|
return( ERROR_NOT_ENOUGH_MEMORY );
|
|
}
|
|
|
|
(*PublicKeyInformation)->Length = PublicKeyInformationLength;
|
|
(*PublicKeyInformation)->KeySourceTag = (ULONG)EfsCertificate;
|
|
|
|
//
|
|
// Copy the string and SID data to the end of the structure.
|
|
//
|
|
|
|
Base = (PWCHAR)(*PublicKeyInformation);
|
|
Base = (PWCHAR)((PBYTE)Base + sizeof( EFS_PUBLIC_KEY_INFO ));
|
|
|
|
if (pUserSid != NULL) {
|
|
|
|
(*PublicKeyInformation)->PossibleKeyOwner = (ULONG)POINTER_TO_OFFSET( Base, *PublicKeyInformation );
|
|
CopySid( UserSidLength, (PSID)Base, pUserSid );
|
|
|
|
} else {
|
|
|
|
(*PublicKeyInformation)->PossibleKeyOwner = 0;
|
|
}
|
|
|
|
Base = (PWCHAR)((PBYTE)Base + UserSidLength);
|
|
|
|
(*PublicKeyInformation)->CertificateInfo.CertificateLength = cbCert;
|
|
(*PublicKeyInformation)->CertificateInfo.Certificate = (ULONG)POINTER_TO_OFFSET( Base, *PublicKeyInformation );
|
|
|
|
memcpy( (PBYTE)Base, pbCert, cbCert );
|
|
|
|
return( ERROR_SUCCESS );
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
CreatePublicKeyInformationThumbprint(
|
|
IN PSID pUserSid OPTIONAL,
|
|
IN PBYTE pbCertHash,
|
|
IN DWORD cbCertHash,
|
|
IN LPWSTR lpDisplayInformation OPTIONAL,
|
|
IN LPWSTR ContainerName OPTIONAL,
|
|
IN LPWSTR ProviderName OPTIONAL,
|
|
OUT PEFS_PUBLIC_KEY_INFO * PublicKeyInformation
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates an EFS_PUBLIC_KEY_INFO structure that can be
|
|
placed in an EFS stream.
|
|
|
|
Arguments:
|
|
|
|
ContainerName - The name of the container containing the public key. This
|
|
parameter is not optional if ProviderName is passed.
|
|
|
|
ProviderName - The name of the provider containing the public key. This
|
|
parameter is not optional if ContainerName is passed.
|
|
|
|
pbPublicKeyBlob - The actual public key blob exported from CryptAPI
|
|
|
|
KeySource - Data for the KeySource field in the public key structure
|
|
|
|
cbPublicKeyBlob - The length of the public key blob in bytes
|
|
|
|
PublicKeyInformation - Returns the filled in EFS_PUBLIC_KEY_INFO
|
|
structure.
|
|
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS or ERROR_NOT_ENOUGH_MEMORY as appropriate
|
|
|
|
--*/
|
|
{
|
|
DWORD PublicKeyInformationLength = 0;
|
|
DWORD cbThumbprint = 0;
|
|
DWORD UserSidLength = 0;
|
|
PWCHAR Base;
|
|
|
|
if (pUserSid != NULL) {
|
|
UserSidLength = GetLengthSid( pUserSid );
|
|
}
|
|
|
|
//
|
|
// Total size is the size of the public key info structure, the size of the
|
|
// cert hash data structure, the length of the thumbprint, and the lengths of the
|
|
// container name and provider name if they were passed.
|
|
//
|
|
|
|
PublicKeyInformationLength = sizeof( EFS_PUBLIC_KEY_INFO ) + UserSidLength;
|
|
|
|
cbThumbprint = sizeof( EFS_CERT_HASH_DATA ) + cbCertHash;
|
|
|
|
if (ContainerName != NULL ) {
|
|
cbThumbprint += (wcslen( ContainerName ) + 1) * sizeof( WCHAR );
|
|
}
|
|
|
|
if (ProviderName != NULL ) {
|
|
cbThumbprint += (wcslen( ProviderName ) + 1) * sizeof( WCHAR );
|
|
}
|
|
|
|
if (lpDisplayInformation != NULL) {
|
|
cbThumbprint += (wcslen( lpDisplayInformation ) + 1) * sizeof( WCHAR );
|
|
}
|
|
|
|
PublicKeyInformationLength += cbThumbprint;
|
|
|
|
//
|
|
// Allocate and fill in the PublicKeyInformation structure
|
|
//
|
|
|
|
*PublicKeyInformation = (PEFS_PUBLIC_KEY_INFO)LsapAllocateLsaHeap( PublicKeyInformationLength );
|
|
|
|
if (*PublicKeyInformation == NULL) {
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
return( FALSE );
|
|
}
|
|
|
|
(*PublicKeyInformation)->Length = PublicKeyInformationLength;
|
|
|
|
//
|
|
// Mark the information as from CryptoAPI, since that's all we support right now.
|
|
//
|
|
|
|
(*PublicKeyInformation)->KeySourceTag = (ULONG)EfsCertificateThumbprint;
|
|
|
|
//
|
|
// Copy the string and SID data to the end of the structure.
|
|
//
|
|
|
|
Base = (PWCHAR)(*PublicKeyInformation);
|
|
Base = (PWCHAR)((PBYTE)Base + sizeof( EFS_PUBLIC_KEY_INFO ));
|
|
|
|
if (pUserSid != NULL) {
|
|
|
|
(*PublicKeyInformation)->PossibleKeyOwner = (ULONG)POINTER_TO_OFFSET( Base, *PublicKeyInformation );
|
|
CopySid( UserSidLength, (PSID)Base, pUserSid );
|
|
|
|
} else {
|
|
|
|
(*PublicKeyInformation)->PossibleKeyOwner = (ULONG)0;
|
|
}
|
|
|
|
Base = (PWCHAR)((PBYTE)Base + UserSidLength);
|
|
|
|
PEFS_CERT_HASH_DATA pCertHashData;
|
|
|
|
(*PublicKeyInformation)->CertificateThumbprint.ThumbprintLength = cbThumbprint;
|
|
(*PublicKeyInformation)->CertificateThumbprint.CertHashData = (ULONG)POINTER_TO_OFFSET( Base, *PublicKeyInformation );
|
|
|
|
pCertHashData = (PEFS_CERT_HASH_DATA)Base;
|
|
|
|
//
|
|
// Zero the header, eliminate the garbage if any of Container, Provide or Display
|
|
// Information is NULL.
|
|
//
|
|
|
|
RtlZeroMemory(pCertHashData, sizeof( EFS_CERT_HASH_DATA ));
|
|
|
|
Base = (PWCHAR)((PBYTE)Base + sizeof( EFS_CERT_HASH_DATA ));
|
|
|
|
//
|
|
// Copy the hash data to the end of the cert hash data block,
|
|
// and set the offset from the *beginning of the cert hash data block
|
|
// (not the beginning of the public key info structure)
|
|
//
|
|
|
|
pCertHashData->cbHash = cbCertHash;
|
|
pCertHashData->pbHash = (ULONG)POINTER_TO_OFFSET( Base, pCertHashData );
|
|
memcpy( (PBYTE)Base, pbCertHash, cbCertHash );
|
|
|
|
Base = (PWCHAR)((PBYTE)Base + cbCertHash);
|
|
|
|
//
|
|
// If we have Container/Provider hint info, copy them in now
|
|
//
|
|
|
|
if (ContainerName != NULL) {
|
|
|
|
pCertHashData->ContainerName = (ULONG)POINTER_TO_OFFSET( Base, pCertHashData );
|
|
wcscpy( (PWCHAR)Base, ContainerName );
|
|
|
|
//
|
|
// wcscpy copies trailing NULL characters, but wcslen doesn't include them in returned lengths,
|
|
// so add 1 to adjust.
|
|
//
|
|
|
|
Base += (wcslen( ContainerName ) + 1);
|
|
|
|
}
|
|
|
|
if (ProviderName != NULL) {
|
|
|
|
//
|
|
// Store the offset into the session key structure
|
|
//
|
|
|
|
pCertHashData->ProviderName = (ULONG)POINTER_TO_OFFSET( Base, pCertHashData );
|
|
wcscpy( (PWCHAR)Base, ProviderName );
|
|
|
|
Base += (wcslen( ProviderName ) + 1);
|
|
|
|
}
|
|
|
|
if (lpDisplayInformation != NULL) {
|
|
pCertHashData->lpDisplayInformation = (ULONG)POINTER_TO_OFFSET( Base, pCertHashData );
|
|
wcscpy( (PWCHAR)Base, lpDisplayInformation );
|
|
|
|
Base += (wcslen( lpDisplayInformation ) + 1);
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
DWORD
|
|
ConstructEncryptedKey(
|
|
PBYTE EncryptedFEK,
|
|
DWORD dwEncryptedFEKLength,
|
|
PEFS_PUBLIC_KEY_INFO PublicKeyInformation,
|
|
PEFS_KEY_SALT pEfsKeySalt OPTIONAL,
|
|
OUT PENCRYPTED_KEY *EncryptedKey,
|
|
OUT PDWORD EncryptedKeySize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine constructs an ENCRYPTED_KEY structure from the passed
|
|
arguments.
|
|
|
|
Arguments:
|
|
|
|
EncryptedFEK - The encrypted FEK.
|
|
|
|
dwEncryptedFEKLength - The length of the encrypted FEK in bytes.
|
|
|
|
PublicKeyInformation - The public key information stucture containing
|
|
the public key.
|
|
|
|
pEfsKeySalt - Salt block.
|
|
|
|
EncryptedKey - Returns the encrypted key structure.
|
|
|
|
EncryptedKeySize - Supplies the length of the encrypted
|
|
key structure. Returns the actual length used or required.
|
|
|
|
Return Value:
|
|
|
|
ERROR_NOT_ENOUGH_MEMORY - Out of memory.
|
|
|
|
ERROR_SUCCESS - Success
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// We now have all the information we need to construct the EncryptedKeys structure
|
|
// Compute the total size required
|
|
//
|
|
|
|
DWORD KeySize = sizeof( ENCRYPTED_KEY ) +
|
|
dwEncryptedFEKLength +
|
|
PublicKeyInformation->Length;
|
|
|
|
if (pEfsKeySalt){
|
|
KeySize += pEfsKeySalt->Length;
|
|
}
|
|
|
|
*EncryptedKey = (PENCRYPTED_KEY) LsapAllocateLsaHeap( KeySize );
|
|
|
|
if ( NULL == *EncryptedKey ) {
|
|
*EncryptedKeySize = 0;
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
*EncryptedKeySize = KeySize;
|
|
|
|
|
|
PBYTE Base;
|
|
|
|
(*EncryptedKey)->Length = *EncryptedKeySize;
|
|
Base = (PBYTE)(((PBYTE)*EncryptedKey) + sizeof( ENCRYPTED_KEY ));
|
|
|
|
//
|
|
// Copy in the public key info structure
|
|
//
|
|
|
|
memcpy( Base, PublicKeyInformation, PublicKeyInformation->Length );
|
|
|
|
//
|
|
// Save offset to what we just copied.
|
|
//
|
|
|
|
(*EncryptedKey)->PublicKeyInfo = (ULONG)POINTER_TO_OFFSET(Base, *EncryptedKey);
|
|
Base += PublicKeyInformation->Length;
|
|
|
|
|
|
//
|
|
// Copy the FEK, which is a completely encrypted structure
|
|
//
|
|
|
|
memcpy( Base, EncryptedFEK, dwEncryptedFEKLength );
|
|
(*EncryptedKey)->EncryptedFEK = (ULONG)POINTER_TO_OFFSET(Base, *EncryptedKey);
|
|
(*EncryptedKey)->EncryptedFEKLength = dwEncryptedFEKLength;
|
|
Base += dwEncryptedFEKLength;
|
|
|
|
//
|
|
// Copy the Salt Information
|
|
//
|
|
|
|
if (pEfsKeySalt){
|
|
memcpy( Base, pEfsKeySalt, pEfsKeySalt->Length );
|
|
(*EncryptedKey)->EfsKeySalt = (ULONG)POINTER_TO_OFFSET(Base, *EncryptedKey);
|
|
} else {
|
|
(*EncryptedKey)->EfsKeySalt = 0;
|
|
}
|
|
|
|
return( ERROR_SUCCESS );
|
|
}
|
|
|
|
|
|
PBYTE
|
|
EncryptFEK(
|
|
IN PEFS_KEY Fek,
|
|
IN HCRYPTKEY hRSAKey,
|
|
OUT PDWORD dwEncryptedFEKLength
|
|
)
|
|
{
|
|
DWORD rc=ERROR_SUCCESS;
|
|
|
|
*dwEncryptedFEKLength = EFS_KEY_SIZE( Fek );
|
|
|
|
//
|
|
// If CryptoAPI worked properly, we wouldn't need this, but it doesn't,
|
|
// so we do.
|
|
//
|
|
|
|
|
|
if (CryptEncrypt( hRSAKey, 0, TRUE, 0, NULL, dwEncryptedFEKLength, 0 )) {
|
|
|
|
DWORD BufferLength = (*dwEncryptedFEKLength < EFS_KEY_SIZE(Fek)) ? EFS_KEY_SIZE(Fek) : *dwEncryptedFEKLength;
|
|
|
|
PBYTE EncryptedFEK = (PBYTE)LsapAllocateLsaHeap( BufferLength );
|
|
|
|
if (EncryptedFEK != NULL) {
|
|
|
|
//
|
|
// Copy the FEK into our new buffer and encrypt it there.
|
|
//
|
|
|
|
memcpy( EncryptedFEK, Fek, EFS_KEY_SIZE( Fek ) );
|
|
|
|
//
|
|
// Reset the length of the data to be encrypted
|
|
//
|
|
|
|
*dwEncryptedFEKLength = EFS_KEY_SIZE( Fek );
|
|
|
|
if (CryptEncrypt( hRSAKey, 0, TRUE, 0, EncryptedFEK, dwEncryptedFEKLength, BufferLength )) {
|
|
return( EncryptedFEK );
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
DebugLog((DEB_ERROR, "EncryptFEK: 2nd CryptEncrypt failed, error = %x\n" , rc ));
|
|
}
|
|
|
|
//
|
|
// If we're here, we failed, clean up
|
|
//
|
|
|
|
LsapFreeLsaHeap( EncryptedFEK );
|
|
|
|
} else {
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
DebugLog((DEB_ERROR, "EncryptFEK: 1st CryptEncrypt failed, error = %x\n" , rc ));
|
|
}
|
|
|
|
if (rc != ERROR_SUCCESS) {
|
|
SetLastError(rc);
|
|
}
|
|
return( NULL );
|
|
}
|
|
|
|
BOOL
|
|
RemoveUsersFromEfsStream(
|
|
IN PEFS_DATA_STREAM_HEADER pEfsStream,
|
|
IN DWORD nHashes,
|
|
IN PENCRYPTION_CERTIFICATE_HASH * pHashes,
|
|
IN PEFS_KEY Fek,
|
|
OUT PEFS_DATA_STREAM_HEADER * pNewEfsStream
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine removes the passed users from the passed EFS
|
|
stream, and returns a new one to be applied to the file.
|
|
|
|
Arguments:
|
|
|
|
argument-name - Supplies | Returns description of argument.
|
|
.
|
|
.
|
|
|
|
Return Value:
|
|
|
|
return-value - Description of conditions needed to return value. - or -
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
//
|
|
// First, see how many matches there are so we can compute
|
|
// the final size of the structure we're going to have to
|
|
// allocate.
|
|
//
|
|
|
|
DWORD cbSizeToDelete = 0;
|
|
DWORD nKeysToDelete = 0;
|
|
DWORD rc = ERROR_SUCCESS;
|
|
BOOL b = FALSE;
|
|
|
|
PENCRYPTED_KEYS pDDF = (PENCRYPTED_KEYS)OFFSET_TO_POINTER( DataDecryptionField, pEfsStream );
|
|
|
|
DWORD KeyCount = pDDF->KeyCount;
|
|
|
|
PDWORD KeyIndiciesToDelete = (PDWORD)LsapAllocateLsaHeap( KeyCount * sizeof( DWORD ));
|
|
|
|
if (KeyIndiciesToDelete) {
|
|
|
|
memset( KeyIndiciesToDelete, 0, KeyCount * sizeof( DWORD ));
|
|
|
|
//
|
|
// First pass: walk the list of keys in the DDF and compare each one to the
|
|
// keys in the list of hashes passed. Count the matches and keep track of the
|
|
// total size of the resulting structure.
|
|
//
|
|
|
|
PENCRYPTED_KEY pEncryptedKey = &pDDF->EncryptedKey[0];
|
|
|
|
for (DWORD i=0; i<KeyCount ; i++, pEncryptedKey = NEXT_ENCRYPTED_KEY( pEncryptedKey )) {
|
|
|
|
|
|
PENCRYPTED_KEY pAlignedKey;
|
|
BOOLEAN freeAlignedKey;
|
|
|
|
rc = EfsAlignBlock(
|
|
pEncryptedKey,
|
|
(PVOID *)&pAlignedKey,
|
|
&freeAlignedKey
|
|
);
|
|
if (!pAlignedKey) {
|
|
|
|
//
|
|
// OOM. Treat it as not current.
|
|
//
|
|
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
nKeysToDelete = 0;
|
|
break;
|
|
|
|
}
|
|
|
|
PEFS_PUBLIC_KEY_INFO pPublicKeyInfo = (PEFS_PUBLIC_KEY_INFO)OFFSET_TO_POINTER( PublicKeyInfo, pAlignedKey );
|
|
|
|
ASSERT( pPublicKeyInfo->KeySourceTag == EfsCertificateThumbprint );
|
|
|
|
PEFS_CERT_HASH_DATA CertHashData = (PEFS_CERT_HASH_DATA)OFFSET_TO_POINTER( CertificateThumbprint.CertHashData, pPublicKeyInfo );
|
|
DWORD cbHash = CertHashData->cbHash;
|
|
PBYTE pbHash = (PBYTE)OFFSET_TO_POINTER( pbHash, CertHashData );
|
|
|
|
//
|
|
// Compare the hash data with all of the data in the array
|
|
//
|
|
|
|
__try{
|
|
|
|
for (DWORD j=0; j<nHashes ; j++) {
|
|
|
|
PENCRYPTION_CERTIFICATE_HASH pHash = pHashes[j];
|
|
PEFS_HASH_BLOB pHashBlob = pHash->pHash;
|
|
|
|
if (pHashBlob->cbData == cbHash ) {
|
|
|
|
if (memcmp( pHashBlob->pbData, pbHash, cbHash ) == 0) {
|
|
|
|
//
|
|
// We have a match. That means that this entry is going to be removed from
|
|
// the DDF when it is rebuilt.
|
|
//
|
|
|
|
cbSizeToDelete += pAlignedKey->Length;
|
|
KeyIndiciesToDelete[nKeysToDelete] = i;
|
|
nKeysToDelete++;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
//
|
|
// The passed in pHashes is bad
|
|
//
|
|
|
|
nKeysToDelete = 0;
|
|
rc = ERROR_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
if (freeAlignedKey) {
|
|
LsapFreeLsaHeap( pAlignedKey );
|
|
}
|
|
|
|
if (ERROR_INVALID_PARAMETER == rc) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (nKeysToDelete != 0) {
|
|
|
|
//
|
|
// We made at least one match. The size of the new efs stream is the size of
|
|
// the old one minus the size of the stuff we're deleting.
|
|
//
|
|
|
|
DWORD cbNewEfsStream = pEfsStream->Length - cbSizeToDelete;
|
|
cbNewEfsStream = (cbNewEfsStream + 7) & 0xfffffff8;
|
|
|
|
*pNewEfsStream = (PEFS_DATA_STREAM_HEADER)LsapAllocateLsaHeap( cbNewEfsStream );
|
|
|
|
if (*pNewEfsStream) {
|
|
|
|
//
|
|
// Copy the old header to the new structure.
|
|
//
|
|
|
|
**pNewEfsStream = *pEfsStream;
|
|
((PEFS_DATA_STREAM_HEADER)*pNewEfsStream)->Length = cbNewEfsStream;
|
|
|
|
//
|
|
// Copy the old DDF to the new DDF, skipping the
|
|
// ones we want to delete
|
|
//
|
|
|
|
//
|
|
// Base is our target. Make it point to the end of the header, which
|
|
// is where we're going to start copying the new DDF.
|
|
//
|
|
|
|
PBYTE Base = (PBYTE) (((PBYTE)(*pNewEfsStream)) + sizeof( EFS_DATA_STREAM_HEADER ));
|
|
|
|
//
|
|
// Set the offset of the new DDF into the header.
|
|
//
|
|
|
|
(*pNewEfsStream)->DataDecryptionField = (ULONG)POINTER_TO_OFFSET( Base, *pNewEfsStream );
|
|
|
|
//
|
|
// Start to assemble the new DDF
|
|
//
|
|
|
|
PENCRYPTED_KEYS pNewDDF = (PENCRYPTED_KEYS)Base;
|
|
pNewDDF->KeyCount = KeyCount - nKeysToDelete;
|
|
|
|
Base = (PBYTE)(&pNewDDF->EncryptedKey[0]);
|
|
PBYTE Source = (PBYTE)(&pDDF->EncryptedKey[0]);
|
|
|
|
DWORD NextKeyIndexToDelete = 0;
|
|
|
|
for (DWORD i=0; i<KeyCount ; i++) {
|
|
|
|
if (KeyIndiciesToDelete[NextKeyIndexToDelete] != i) {
|
|
|
|
//
|
|
// We're not going to delete this one, copy it.
|
|
//
|
|
|
|
DWORD KeyLength = * (DWORD UNALIGNED *) &(((PENCRYPTED_KEY)Source)->Length);
|
|
|
|
memcpy( Base, Source, KeyLength );
|
|
|
|
Base = (PBYTE)NEXT_ENCRYPTED_KEY( Base );
|
|
Source = (PBYTE)NEXT_ENCRYPTED_KEY( Source );
|
|
|
|
} else {
|
|
|
|
//
|
|
// We're going to delete this one. Leave Base
|
|
// alone, but bump Source to the next key.
|
|
//
|
|
|
|
Source = (PBYTE)NEXT_ENCRYPTED_KEY( Source );
|
|
NextKeyIndexToDelete++;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// The new DDF is in place. Copy the recovery information
|
|
// from the old EFS stream into the new one.
|
|
//
|
|
// Base points to where the DRF needs to go, and Source
|
|
// points to where the old one is (in theory).
|
|
//
|
|
|
|
ASSERT( Source == (PBYTE)OFFSET_TO_POINTER( DataRecoveryField, pEfsStream ));
|
|
|
|
PENCRYPTED_KEYS pDRF = (PENCRYPTED_KEYS)Source;
|
|
|
|
//
|
|
// Set the offset of the new DRF into the new EFS stream.
|
|
//
|
|
|
|
if ((PVOID)pDRF == (PVOID)pEfsStream) {
|
|
|
|
//
|
|
// No DRF in the old $EFS
|
|
//
|
|
|
|
(*pNewEfsStream)->DataRecoveryField = 0;
|
|
|
|
} else {
|
|
|
|
(*pNewEfsStream)->DataRecoveryField = (ULONG)POINTER_TO_OFFSET( Base, *pNewEfsStream );
|
|
|
|
//
|
|
// We can copy the old DRF directly into the new one, since nothing in
|
|
// it is changing.
|
|
//
|
|
|
|
//
|
|
// Base now points to the top of an ENCRYPTED_KEYS structure.
|
|
// Fill in its header.
|
|
//
|
|
|
|
PENCRYPTED_KEYS pNewDRF = (PENCRYPTED_KEYS)Base;
|
|
RtlCopyMemory(&(pNewDRF->KeyCount), &(pDRF->KeyCount), sizeof(ULONG));
|
|
RtlCopyMemory(&KeyCount, &(pDRF->KeyCount), sizeof(ULONG));
|
|
//
|
|
// That was the header. Now start copying the
|
|
// encrypted keys themselves.
|
|
//
|
|
|
|
Base = (PBYTE)(&pNewDRF->EncryptedKey[0]);
|
|
Source = (PBYTE)(&pDRF->EncryptedKey[0]);
|
|
|
|
for (i=0; i<KeyCount ; i++) {
|
|
|
|
DWORD KeyLength = * (DWORD UNALIGNED *) &(((PENCRYPTED_KEY)Source)->Length);
|
|
|
|
memcpy( Base, Source, KeyLength );
|
|
|
|
Base = (PBYTE)NEXT_ENCRYPTED_KEY( Base );
|
|
Source = (PBYTE)NEXT_ENCRYPTED_KEY( Source );
|
|
}
|
|
|
|
}
|
|
|
|
b = TRUE;
|
|
|
|
|
|
// memset( &((*pNewEfsStream)->EfsHash), 0, MD5_HASH_SIZE );
|
|
// if (EfspChecksumEfs( *pNewEfsStream, Fek )) {
|
|
// b = TRUE;
|
|
// }
|
|
|
|
} else {
|
|
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
} else {
|
|
|
|
if (ERROR_SUCCESS == rc) {
|
|
b = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
LsapFreeLsaHeap( KeyIndiciesToDelete );
|
|
|
|
} else {
|
|
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
}
|
|
|
|
if (!b) {
|
|
|
|
//
|
|
// Something failed, clean up what we were going
|
|
// to return.
|
|
//
|
|
|
|
if (*pNewEfsStream) {
|
|
LsapFreeLsaHeap( *pNewEfsStream );
|
|
*pNewEfsStream = NULL; // paranoia
|
|
}
|
|
}
|
|
|
|
SetLastError(rc);
|
|
return( b );
|
|
}
|
|
|
|
|
|
BOOL
|
|
QueryCertsFromEncryptedKeys(
|
|
IN PENCRYPTED_KEYS pEncryptedKeys,
|
|
OUT PDWORD pnUsers,
|
|
OUT PENCRYPTION_CERTIFICATE_HASH ** pHashes
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes a set of encrypted keys and returns the data
|
|
that we wish to display about each one.
|
|
|
|
Arguments:
|
|
|
|
pEncryptedKeys - Supplies the array of encrypted keys.
|
|
|
|
pnUsers - Returns the number of users on the file.
|
|
|
|
pHashes - Returns the hash information about each user.
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure. Call GetLastError() for more details.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD rc = ERROR_SUCCESS;
|
|
PENCRYPTION_CERTIFICATE_HASH pTmp = NULL;
|
|
|
|
//
|
|
// Walk the entries in the encrypted keys and return the information we want about each one.
|
|
//
|
|
|
|
DWORD KeyCount = * (DWORD UNALIGNED *) &(pEncryptedKeys->KeyCount);
|
|
*pnUsers = KeyCount;
|
|
PENCRYPTED_KEY pEncryptedKey = &pEncryptedKeys->EncryptedKey[0];
|
|
|
|
//
|
|
// *pHashes points to an array of pointers to ENCRYPTION_CERTIFICATE_HASH structures.
|
|
// There will be one entry for each Key on the file
|
|
//
|
|
|
|
*pHashes = (PENCRYPTION_CERTIFICATE_HASH *)MIDL_user_allocate( sizeof(PENCRYPTION_CERTIFICATE_HASH) * KeyCount );
|
|
|
|
if (*pHashes) {
|
|
|
|
memset( *pHashes, 0, sizeof(PENCRYPTION_CERTIFICATE_HASH) * KeyCount );
|
|
|
|
for (DWORD i=0;
|
|
i < KeyCount;
|
|
i++, pEncryptedKey = NEXT_ENCRYPTED_KEY( pEncryptedKey )
|
|
) {
|
|
|
|
|
|
PENCRYPTED_KEY pAlignedKey;
|
|
BOOLEAN freeAlignedKey;
|
|
|
|
rc = EfsAlignBlock(
|
|
pEncryptedKey,
|
|
(PVOID *)&pAlignedKey,
|
|
&freeAlignedKey
|
|
);
|
|
if (!pAlignedKey) {
|
|
|
|
//
|
|
// OOM. Treat it as not current.
|
|
//
|
|
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
|
|
}
|
|
|
|
PEFS_PUBLIC_KEY_INFO pPublicKeyInfo = (PEFS_PUBLIC_KEY_INFO)OFFSET_TO_POINTER( PublicKeyInfo, pAlignedKey );
|
|
|
|
ASSERT( pPublicKeyInfo->KeySourceTag == EfsCertificateThumbprint );
|
|
|
|
PENCRYPTION_CERTIFICATE_HASH pTmp = (PENCRYPTION_CERTIFICATE_HASH) MIDL_user_allocate( sizeof(ENCRYPTION_CERTIFICATE_HASH ));
|
|
|
|
if (pTmp) {
|
|
|
|
memset( pTmp, 0, sizeof( ENCRYPTION_CERTIFICATE_HASH ));
|
|
|
|
pTmp->cbTotalLength = sizeof( ENCRYPTION_CERTIFICATE_HASH );
|
|
|
|
if (pPublicKeyInfo->PossibleKeyOwner) {
|
|
|
|
PSID pUserSid = ( PSID )OFFSET_TO_POINTER( PossibleKeyOwner, pPublicKeyInfo );
|
|
|
|
pTmp->pUserSid = (SID *)MIDL_user_allocate( GetLengthSid( pUserSid ));
|
|
|
|
if (pTmp->pUserSid) {
|
|
|
|
CopySid( GetLengthSid( pUserSid ),
|
|
pTmp->pUserSid,
|
|
pUserSid
|
|
);
|
|
} else {
|
|
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
} else {
|
|
|
|
pTmp->pUserSid = NULL;
|
|
}
|
|
|
|
//
|
|
// Copy the hash
|
|
//
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
pTmp->pHash = (PEFS_HASH_BLOB)MIDL_user_allocate( sizeof( EFS_HASH_BLOB ));
|
|
|
|
if (pTmp->pHash) {
|
|
|
|
PEFS_CERT_HASH_DATA CertHashData = (PEFS_CERT_HASH_DATA)OFFSET_TO_POINTER( CertificateThumbprint.CertHashData, pPublicKeyInfo );
|
|
|
|
pTmp->pHash->cbData = CertHashData->cbHash;
|
|
pTmp->pHash->pbData = (PBYTE)MIDL_user_allocate( pTmp->pHash->cbData );
|
|
|
|
if (pTmp->pHash->pbData) {
|
|
|
|
memcpy( pTmp->pHash->pbData, OFFSET_TO_POINTER( pbHash, CertHashData ), pTmp->pHash->cbData );
|
|
ASSERT( rc == ERROR_SUCCESS );
|
|
|
|
} else {
|
|
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Allocate and fill in the display information field.
|
|
//
|
|
|
|
if (CertHashData->lpDisplayInformation) {
|
|
|
|
LPWSTR lpDisplayInformation = (LPWSTR)OFFSET_TO_POINTER( lpDisplayInformation, CertHashData );
|
|
|
|
pTmp->lpDisplayInformation = (LPWSTR)MIDL_user_allocate( (wcslen( lpDisplayInformation ) + 1) * sizeof( WCHAR ));
|
|
|
|
if (pTmp->lpDisplayInformation) {
|
|
|
|
wcscpy( pTmp->lpDisplayInformation, lpDisplayInformation );
|
|
(pTmp->lpDisplayInformation)[wcslen(lpDisplayInformation)] = UNICODE_NULL;
|
|
|
|
ASSERT( rc == ERROR_SUCCESS );
|
|
|
|
} else {
|
|
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
if (rc != ERROR_SUCCESS) {
|
|
|
|
//
|
|
// We couldn't successfully build this structure. Free up
|
|
// all the stuff we were going to return and drop out of the loop.
|
|
//
|
|
|
|
if (pTmp) {
|
|
if (pTmp->pHash) {
|
|
if (pTmp->pHash->pbData) {
|
|
MIDL_user_free( pTmp->pHash->pbData );
|
|
}
|
|
MIDL_user_free( pTmp->pHash );
|
|
}
|
|
|
|
if (pTmp->lpDisplayInformation) {
|
|
MIDL_user_free( pTmp->lpDisplayInformation );
|
|
}
|
|
|
|
if (pTmp->pUserSid) {
|
|
MIDL_user_free( pTmp->pUserSid );
|
|
}
|
|
|
|
MIDL_user_free( pTmp );
|
|
}
|
|
|
|
if (freeAlignedKey) {
|
|
LsapFreeLsaHeap( pAlignedKey );
|
|
}
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
(*pHashes)[i] = pTmp;
|
|
if (freeAlignedKey) {
|
|
LsapFreeLsaHeap( pAlignedKey );
|
|
}
|
|
}
|
|
}
|
|
|
|
if (rc != ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Something failed along the way, walk down the list of ones
|
|
// we successfully allocated and free them up. Any partially
|
|
// allocated ones will have been cleaned up above.
|
|
//
|
|
|
|
DWORD i=0;
|
|
|
|
while ( (*pHashes)[i] ) {
|
|
|
|
pTmp = (*pHashes)[i];
|
|
|
|
ASSERT( pTmp->pHash );
|
|
ASSERT( pTmp->pHash->pbData );
|
|
|
|
MIDL_user_free( pTmp->pHash->pbData );
|
|
MIDL_user_free( pTmp->pHash );
|
|
if (pTmp->lpDisplayInformation) {
|
|
MIDL_user_free( pTmp->lpDisplayInformation );
|
|
}
|
|
if (pTmp->pUserSid) {
|
|
MIDL_user_free( pTmp->pUserSid );
|
|
}
|
|
MIDL_user_free( pTmp );
|
|
|
|
(*pHashes)[i] = NULL;
|
|
i++;
|
|
}
|
|
|
|
MIDL_user_free( *pHashes );
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
SetLastError( rc );
|
|
|
|
return( ERROR_SUCCESS == rc ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
BOOL
|
|
EfsErrorToNtStatus(
|
|
IN DWORD WinError,
|
|
OUT PNTSTATUS NtStatus
|
|
)
|
|
{
|
|
switch (WinError) {
|
|
case ERROR_ENCRYPTION_FAILED:
|
|
{
|
|
*NtStatus = STATUS_ENCRYPTION_FAILED;
|
|
break;
|
|
}
|
|
case NTE_BAD_KEYSET:
|
|
case CRYPT_E_NOT_FOUND:
|
|
case ERROR_DECRYPTION_FAILED:
|
|
{
|
|
*NtStatus = STATUS_DECRYPTION_FAILED;
|
|
break;
|
|
}
|
|
case ERROR_FILE_ENCRYPTED:
|
|
{
|
|
*NtStatus = STATUS_FILE_ENCRYPTED;
|
|
break;
|
|
}
|
|
case ERROR_NO_RECOVERY_POLICY:
|
|
{
|
|
*NtStatus = STATUS_NO_RECOVERY_POLICY;
|
|
break;
|
|
}
|
|
case ERROR_NO_EFS:
|
|
{
|
|
*NtStatus = STATUS_NO_EFS;
|
|
break;
|
|
}
|
|
case ERROR_WRONG_EFS:
|
|
{
|
|
*NtStatus = STATUS_WRONG_EFS;
|
|
break;
|
|
}
|
|
case ERROR_NO_USER_KEYS:
|
|
{
|
|
*NtStatus = STATUS_NO_USER_KEYS;
|
|
break;
|
|
}
|
|
case ERROR_FILE_NOT_ENCRYPTED:
|
|
{
|
|
*NtStatus = STATUS_FILE_NOT_ENCRYPTED;
|
|
break;
|
|
}
|
|
case ERROR_NOT_EXPORT_FORMAT:
|
|
{
|
|
*NtStatus = STATUS_NOT_EXPORT_FORMAT;
|
|
break;
|
|
}
|
|
case ERROR_OUTOFMEMORY:
|
|
{
|
|
*NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
case ERROR_ACCESS_DENIED:
|
|
{
|
|
*NtStatus = STATUS_ACCESS_DENIED;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
DebugLog((DEB_WARN, "EfsErrorToNtStatus, unable to translate 0x%x\n" , WinError ));
|
|
return( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
VOID
|
|
DumpBytes(
|
|
PBYTE Blob,
|
|
ULONG Length,
|
|
ULONG IndentLevel
|
|
)
|
|
{
|
|
|
|
const UINT Columns = 8;
|
|
UINT Rows = Length / Columns;
|
|
if (Length % Columns != 0) {
|
|
Rows++;
|
|
}
|
|
|
|
for (UINT j=0; j<Rows ; j++) {
|
|
for (UINT k=0; k<IndentLevel ; k++) {
|
|
DbgPrint("\t");
|
|
}
|
|
for (UINT i=0; i<Columns ; i++) {
|
|
DbgPrint("%02X ",Blob[ j*Columns + i ]);
|
|
if ((j*Columns + i) == Length) {
|
|
break;
|
|
}
|
|
}
|
|
DbgPrint("\n");
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
DumpPublicKeyInfo(
|
|
PEFS_PUBLIC_KEY_INFO PublicKeyInfo
|
|
)
|
|
{
|
|
DbgPrint("\t\tPublicKeyInfo:\n");
|
|
DbgPrint("\t\tLength = 0x%x\n",PublicKeyInfo->Length);
|
|
|
|
if (PublicKeyInfo->PossibleKeyOwner != NULL) {
|
|
|
|
PWCHAR SidString = ConvertSidToWideCharString( OFFSET_TO_POINTER( PossibleKeyOwner, PublicKeyInfo) );
|
|
DbgPrint("\t\tUserSid = %ws\n",SidString);
|
|
LsapFreeLsaHeap( SidString );
|
|
|
|
} else {
|
|
|
|
DbgPrint("\t\tUserSid = NULL\n");
|
|
}
|
|
|
|
switch (PublicKeyInfo->KeySourceTag) {
|
|
case EfsCryptoAPIContainer:
|
|
{
|
|
DbgPrint("\t\tTag = EfsCryptoAPIContainer\n");
|
|
DbgPrint("\t\tContainerName = %ws\n",OFFSET_TO_POINTER( ContainerInfo.ContainerName, PublicKeyInfo ));
|
|
DbgPrint("\t\tProviderName = %ws\n",OFFSET_TO_POINTER( ContainerInfo.ProviderName, PublicKeyInfo ));
|
|
|
|
DbgPrint("\t\tPublicKeyBlobLength = 0x%x\n",PublicKeyInfo->ContainerInfo.PublicKeyBlobLength);
|
|
|
|
DumpBytes( (PBYTE)OFFSET_TO_POINTER( ContainerInfo.PublicKeyBlob, PublicKeyInfo ), PublicKeyInfo->ContainerInfo.PublicKeyBlobLength, 2 );
|
|
|
|
break;
|
|
}
|
|
case EfsCertificateThumbprint:
|
|
{
|
|
|
|
DbgPrint("\t\tTag = EfsCertificateThumbprint\n");
|
|
PEFS_CERT_HASH_DATA CertHashData = (PEFS_CERT_HASH_DATA)OFFSET_TO_POINTER( CertificateThumbprint.CertHashData, PublicKeyInfo );
|
|
|
|
LPWSTR ContainerName = NULL;
|
|
|
|
if (CertHashData->ContainerName) {
|
|
LPWSTR ContainerName = (LPWSTR)OFFSET_TO_POINTER( ContainerName ,CertHashData);
|
|
DbgPrint("\t\tContainerName = %ws\n",ContainerName);
|
|
} else {
|
|
DbgPrint("\t\tContainerName = NULL\n");
|
|
}
|
|
|
|
LPWSTR ProviderName = NULL;
|
|
|
|
if (CertHashData->ProviderName) {
|
|
LPWSTR ProviderName = (LPWSTR)OFFSET_TO_POINTER( ProviderName ,CertHashData);
|
|
DbgPrint("\t\tProviderName = %ws\n",ProviderName);
|
|
} else {
|
|
DbgPrint("\t\tProviderName = NULL\n");
|
|
}
|
|
|
|
LPWSTR lpDisplayInformation = NULL;
|
|
|
|
if (CertHashData->lpDisplayInformation) {
|
|
LPWSTR lpDisplayInformation = (LPWSTR)OFFSET_TO_POINTER( lpDisplayInformation ,CertHashData);
|
|
DbgPrint("\t\tlpDisplayInformation = %ws\n",lpDisplayInformation);
|
|
} else {
|
|
DbgPrint("\t\tlpDisplayInformation = NULL\n");
|
|
}
|
|
|
|
DbgPrint("\t\tcbHash = 0x%x\n",CertHashData->cbHash );
|
|
DbgPrint("\t\tpbHash = \n");
|
|
PBYTE pbHash = (PBYTE)OFFSET_TO_POINTER( pbHash, CertHashData );
|
|
DumpBytes( pbHash, CertHashData->cbHash, 2);
|
|
break;
|
|
}
|
|
case EfsCertificate:
|
|
{
|
|
DbgPrint("KeySourceTag of EfsCertificate unexpected\n");
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DbgPrint("Unknown KeySourceTag value: %d\n",PublicKeyInfo->KeySourceTag );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
DumpEncryptedKey(
|
|
PENCRYPTED_KEY EncryptedKey
|
|
)
|
|
{
|
|
DbgPrint("\tLength = 0x%x\n",EncryptedKey->Length);
|
|
|
|
PEFS_PUBLIC_KEY_INFO PublicKeyInfo = (PEFS_PUBLIC_KEY_INFO)OFFSET_TO_POINTER( PublicKeyInfo, EncryptedKey );
|
|
DumpPublicKeyInfo( PublicKeyInfo );
|
|
|
|
DbgPrint("\tEncryptedFEKLength = 0x%x\n",EncryptedKey->EncryptedFEKLength);
|
|
DbgPrint("\tEncryptedFEK = \n");
|
|
DumpBytes( (PBYTE)OFFSET_TO_POINTER( EncryptedFEK, EncryptedKey ), EncryptedKey->EncryptedFEKLength, 1 );
|
|
|
|
}
|
|
|
|
|
|
void
|
|
DumpRecoveryKey(
|
|
PRECOVERY_KEY_1_1 pRecoveryKey
|
|
)
|
|
{
|
|
DbgPrint("\nRecovery key @ 0x%x\n",pRecoveryKey);
|
|
|
|
DbgPrint("Length = 0x%x\n",pRecoveryKey->TotalLength);
|
|
DbgPrint("PublicKeyInfo:\n");
|
|
DumpPublicKeyInfo( &pRecoveryKey->PublicKeyInfo );
|
|
}
|
|
|
|
|
|
VOID
|
|
DumpEFS(
|
|
PEFS_DATA_STREAM_HEADER Efs
|
|
)
|
|
{
|
|
DbgPrint("\nEFS @ 0x%x:\n",Efs);
|
|
|
|
|
|
DbgPrint("Length = \t\t0x%x\n",Efs->Length);
|
|
DbgPrint("State = \t\t%d\n",Efs->State);
|
|
DbgPrint("EfsVersion = \t\t%d\n",Efs->EfsVersion);
|
|
DbgPrint("CryptoApiVersion = \t%d\n",Efs->CryptoApiVersion);
|
|
DbgPrint("Offset to DDF = \t0x%x\n",Efs->DataDecryptionField);
|
|
DbgPrint("Offset to DRF = \t0x%x\n",Efs->DataRecoveryField);
|
|
DbgPrint("Reserved = \t0x%x\n",Efs->Reserved);
|
|
|
|
DbgPrint("\nDDF:\n");
|
|
|
|
PENCRYPTED_KEYS Ddf = (PENCRYPTED_KEYS)OFFSET_TO_POINTER( DataDecryptionField, Efs );
|
|
|
|
DbgPrint("Number of keys = %d\n",Ddf->KeyCount );
|
|
|
|
UINT i;
|
|
|
|
PENCRYPTED_KEY EncryptedKey = &Ddf->EncryptedKey[0];
|
|
for (i=0; i<Ddf->KeyCount; i++) {
|
|
DbgPrint("\tKey %d:\n",i);
|
|
DumpEncryptedKey( EncryptedKey );
|
|
EncryptedKey = (PENCRYPTED_KEY)(((PBYTE)EncryptedKey) + EncryptedKey->Length);
|
|
}
|
|
|
|
DbgPrint("\nDRF:\n");
|
|
|
|
PENCRYPTED_KEYS Drf = (PENCRYPTED_KEYS)OFFSET_TO_POINTER( DataRecoveryField, Efs );
|
|
|
|
DbgPrint("Number of keys = %d\n",Drf->KeyCount );
|
|
|
|
EncryptedKey = &Drf->EncryptedKey[0];
|
|
for (i=0; i<Drf->KeyCount; i++) {
|
|
DbgPrint("\tKey %d:\n",i);
|
|
DumpEncryptedKey( EncryptedKey );
|
|
EncryptedKey = (PENCRYPTED_KEY)(((PBYTE)EncryptedKey) + EncryptedKey->Length);
|
|
}
|
|
}
|
|
|
|
|
|
#if 0
|
|
|
|
BOOLEAN
|
|
EfspChecksumEfs(
|
|
PEFS_DATA_STREAM_HEADER pEFS,
|
|
PEFS_KEY Fek
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will checksum the passed EFS stream and fill in the checksum
|
|
field in the header. Assumes that the checksum field itself is set to 0.
|
|
|
|
Arguments:
|
|
|
|
pEFS - Supplies the EFS stream to be checksum'd. Assume that this structure
|
|
has been fully filled in.
|
|
|
|
Fek - Supplies a pointer to the FEK for the file.
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure. Sets LastErrror.
|
|
|
|
--*/
|
|
|
|
{
|
|
HCRYPTHASH hHash = 0;
|
|
DWORD dwHashedDataLength = MD5_HASH_SIZE;
|
|
BOOL b = FALSE;
|
|
DWORD rc = ERROR_SUCCESS;
|
|
|
|
|
|
if (CryptCreateHash( hProvVerify, CALG_MD5, 0, 0, &hHash )) {
|
|
|
|
if (CryptHashData( hHash, (PBYTE)pEFS, pEFS->Length, 0 )) {
|
|
|
|
if (CryptHashData( hHash, EFS_KEY_DATA( Fek ), Fek->KeyLength, 0 )) {
|
|
|
|
if (CryptGetHashParam( hHash, HP_HASHVAL, (PBYTE)(&pEFS->EfsHash), &dwHashedDataLength, 0 )) {
|
|
|
|
ASSERT( dwHashedDataLength == MD5_HASH_SIZE );
|
|
|
|
b = TRUE;
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
ASSERT( rc != ERROR_SUCCESS );
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
ASSERT( rc != ERROR_SUCCESS );
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
ASSERT( rc != ERROR_SUCCESS );
|
|
}
|
|
|
|
CryptDestroyHash( hHash );
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
ASSERT( rc != ERROR_SUCCESS );
|
|
}
|
|
|
|
|
|
SetLastError( rc );
|
|
|
|
return( b != 0);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
EfspValidateEfsStream(
|
|
PEFS_DATA_STREAM_HEADER pEFS,
|
|
PEFS_KEY Fek
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks the checksum in an EFS stream.
|
|
|
|
Arguments:
|
|
|
|
pEFS - Supplies the EFS stream to be validated.
|
|
|
|
Fek - Supplies the FEK of the encrypted file.
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure.
|
|
|
|
--*/
|
|
{
|
|
DWORD dwHashSize = MD5_HASH_SIZE ;
|
|
|
|
UCHAR SavedChecksum[ MD5_HASH_SIZE ];
|
|
UCHAR NewCheckSum [ MD5_HASH_SIZE ];
|
|
|
|
BOOL b = FALSE;
|
|
HCRYPTHASH hHash = 0;
|
|
HCRYPTPROV hProv;
|
|
|
|
DWORD rc = ERROR_SUCCESS;
|
|
|
|
//
|
|
// We have to do the checksum with the checksum in the header
|
|
// zero'd out. Save the original in a local.
|
|
//
|
|
|
|
memcpy( SavedChecksum, &pEFS->EfsHash, MD5_HASH_SIZE );
|
|
memset( &pEFS->EfsHash, 0, MD5_HASH_SIZE );
|
|
|
|
if (CryptCreateHash( hProvVerify, CALG_MD5, 0, 0, &hHash )) {
|
|
|
|
if (CryptHashData( hHash, (PBYTE)pEFS, pEFS->Length, 0 )) {
|
|
|
|
if (CryptHashData( hHash, EFS_KEY_DATA( Fek ), Fek->KeyLength, 0 )) {
|
|
|
|
if (CryptGetHashParam( hHash, HP_HASHVAL, NewCheckSum, &dwHashSize, 0 )) {
|
|
|
|
ASSERT( dwHashSize == MD5_HASH_SIZE );
|
|
|
|
if (memcmp( NewCheckSum, SavedChecksum, MD5_HASH_SIZE ) == 0) {
|
|
b = TRUE;
|
|
} else {
|
|
rc = ERROR_ACCESS_DENIED;
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
ASSERT( rc != ERROR_SUCCESS );
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
ASSERT( rc != ERROR_SUCCESS );
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
ASSERT( rc != ERROR_SUCCESS );
|
|
}
|
|
|
|
CryptDestroyHash( hHash );
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
ASSERT( rc != ERROR_SUCCESS );
|
|
}
|
|
|
|
|
|
//
|
|
// Copy back the original
|
|
//
|
|
|
|
memcpy( &pEFS->EfsHash, SavedChecksum, MD5_HASH_SIZE );
|
|
|
|
SetLastError( rc );
|
|
|
|
return( b != 0);
|
|
}
|
|
|
|
#endif
|
|
|
|
BOOL
|
|
GetSaltLength(
|
|
ALG_ID AlgID,
|
|
DWORD *SaltLength,
|
|
DWORD *SaltBlockLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns the length of key salt
|
|
|
|
Arguments:
|
|
|
|
AlgID - Encryption Algorithm ID.
|
|
|
|
SaltLength - Bytes to be copied from key.
|
|
|
|
SaltBlockLength - Bytes of key salt block in $EFS
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure.
|
|
--*/
|
|
{
|
|
BOOL b = FALSE;
|
|
|
|
switch (AlgID){
|
|
case CALG_DESX:
|
|
*SaltLength = EXPORT_DESX_SALT_LENGTH;
|
|
*SaltBlockLength = (EXPORT_DESX_SALT_LENGTH + 4 ) & 0xfffffffc;
|
|
b = TRUE;
|
|
break;
|
|
default:
|
|
*SaltLength = 0;
|
|
*SaltBlockLength = 0;
|
|
break;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
|
|
VOID
|
|
EfspUnloadUserProfile(
|
|
IN HANDLE hToken,
|
|
IN HANDLE hProfile
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cleans up after a call to EfspLoadUserProfile. Returns impersonating our client.
|
|
|
|
Arguments:
|
|
|
|
hToken - The token handle returned from EfspLoadUserProfile. This handle will be closed!
|
|
|
|
hProfile - The profile handle returned from EfspLoadUserProfile. This handle will not
|
|
be modified.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
if (!hToken) {
|
|
|
|
//
|
|
// SYSTEM context. The profile was not loaded by EFS.
|
|
//
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
RevertToSelf();
|
|
|
|
(VOID) UnloadUserProfile (hToken, hProfile);
|
|
|
|
//
|
|
// Start impersonating again
|
|
//
|
|
|
|
Status = NtSetInformationThread(
|
|
NtCurrentThread(),
|
|
ThreadImpersonationToken,
|
|
(PVOID) &hToken,
|
|
sizeof(HANDLE)
|
|
);
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
DebugLog((DEB_ERROR, "EfspUnloadUserProfile: NtSetInformationThread returned %x\n" ,Status ));
|
|
}
|
|
|
|
NtClose( hToken );
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
EfspFreeUserCache(
|
|
IN PUSER_CACHE pUserCache
|
|
)
|
|
{
|
|
if (pUserCache == NULL){
|
|
return;
|
|
}
|
|
if (pUserCache->pbHash) {
|
|
|
|
LsapFreeLsaHeap( pUserCache->pbHash );
|
|
|
|
}
|
|
|
|
if (pUserCache->ContainerName) {
|
|
|
|
LsapFreeLsaHeap( pUserCache->ContainerName );
|
|
|
|
}
|
|
if (pUserCache->DisplayInformation) {
|
|
|
|
LsapFreeLsaHeap( pUserCache->DisplayInformation );
|
|
|
|
}
|
|
if (pUserCache->ProviderName) {
|
|
|
|
LsapFreeLsaHeap( pUserCache->ProviderName );
|
|
|
|
}
|
|
if (pUserCache->pCertContext) {
|
|
|
|
CertFreeCertificateContext( pUserCache->pCertContext );
|
|
|
|
}
|
|
if (pUserCache->hUserKey) {
|
|
|
|
CryptDestroyKey( pUserCache->hUserKey );
|
|
|
|
}
|
|
if (pUserCache->hProv) {
|
|
|
|
CryptReleaseContext( pUserCache->hProv, 0 );
|
|
|
|
}
|
|
|
|
LsapFreeLsaHeap( pUserCache );
|
|
|
|
}
|
|
|
|
VOID
|
|
EfspReleaseUserCache(
|
|
IN PUSER_CACHE pUserCache
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Decrease the ref count increased by the EfspGetUserCache
|
|
|
|
Arguments:
|
|
|
|
pUserCache - Cache node
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
RtlEnterCriticalSection( &GuardCacheListLock );
|
|
pUserCache->UseRefCount-- ;
|
|
RtlLeaveCriticalSection( &GuardCacheListLock );
|
|
}
|
|
|
|
PUSER_CACHE
|
|
EfspGetUserCache(
|
|
IN OUT PEFS_USER_INFO pEfsUserInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will try to find the user's cert info in the cache list.
|
|
|
|
If return not NULL, this call must be balanced with a EfspReleaseUserCache(PUSER_CACHE pUserCache)
|
|
|
|
Arguments:
|
|
|
|
pEfsUserInfo - User Info
|
|
|
|
Return Value:
|
|
|
|
User cache list node, if match found in the cache.
|
|
NULL if not found.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY pListHead, pLink;
|
|
PUSER_CACHE pUserCache;
|
|
|
|
//
|
|
// Check to see if there is cache available
|
|
//
|
|
|
|
pEfsUserInfo->UserCacheStop = FALSE;
|
|
|
|
RtlEnterCriticalSection( &GuardCacheListLock );
|
|
|
|
if (UserCacheList.Flink == &UserCacheList) {
|
|
|
|
//
|
|
// list empty
|
|
//
|
|
|
|
RtlLeaveCriticalSection( &GuardCacheListLock );
|
|
return NULL;
|
|
}
|
|
for (pLink = UserCacheList.Flink; pLink != &UserCacheList; pLink = pLink->Flink) {
|
|
pUserCache = CONTAINING_RECORD(pLink, USER_CACHE, CacheChain);
|
|
|
|
ASSERT( pLink );
|
|
ASSERT( pLink->Flink );
|
|
|
|
if ( (pEfsUserInfo->AuthId.LowPart == pUserCache->AuthId.LowPart) &&
|
|
(pEfsUserInfo->AuthId.HighPart == pUserCache->AuthId.HighPart)) {
|
|
|
|
//
|
|
// Find the cache node. Hold it
|
|
//
|
|
|
|
if (pUserCache->StopUseCount) {
|
|
|
|
//
|
|
// Free cache waiting
|
|
// When cache for a session is stopped, both interactive and non-interactive should be stopped
|
|
//
|
|
|
|
pEfsUserInfo->UserCacheStop = TRUE;
|
|
|
|
RtlLeaveCriticalSection( &GuardCacheListLock );
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
pUserCache->UseRefCount++;
|
|
RtlLeaveCriticalSection( &GuardCacheListLock );
|
|
return pUserCache;
|
|
|
|
}
|
|
|
|
}
|
|
RtlLeaveCriticalSection( &GuardCacheListLock );
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
EfspAddUserCache(
|
|
IN PUSER_CACHE pUserCache
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will try to add the user's cert info in the cache list.
|
|
|
|
If return TRUE, this call must be balanced with a EfspReleaseUserCache(PUSER_CACHE pUserCache)
|
|
|
|
Arguments:
|
|
|
|
pUserCache - User Cache node.
|
|
|
|
Return Value:
|
|
|
|
TRUE if added successfully
|
|
FALSE if the list is full.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY pListHead, pLink;
|
|
PUSER_CACHE pUserTmpCache;
|
|
|
|
RtlEnterCriticalSection( &GuardCacheListLock );
|
|
|
|
if (UserCacheListCount >= UserCacheListLimit) {
|
|
|
|
//
|
|
// Let's see if we can kick someone out.
|
|
//
|
|
|
|
pLink = UserCacheList.Blink;
|
|
while ( pLink != &UserCacheList ){
|
|
|
|
pUserTmpCache = CONTAINING_RECORD(pLink, USER_CACHE, CacheChain);
|
|
|
|
ASSERT( pLink );
|
|
ASSERT( pLink->Blink );
|
|
|
|
pLink = pLink->Blink;
|
|
if ( pUserTmpCache->UseRefCount <= 0 ){
|
|
|
|
//
|
|
// No one is using it. Let's remove it.
|
|
//
|
|
|
|
RemoveEntryList(&( pUserTmpCache->CacheChain ));
|
|
UserCacheListCount--;
|
|
EfspFreeUserCache( pUserTmpCache );
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
if (UserCacheListCount >= UserCacheListLimit) {
|
|
RtlLeaveCriticalSection( &GuardCacheListLock );
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
InsertHeadList(&UserCacheList, &( pUserCache->CacheChain ));
|
|
UserCacheListCount++;
|
|
|
|
RtlLeaveCriticalSection( &GuardCacheListLock );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
EfspGetUserInfo(
|
|
IN OUT PEFS_USER_INFO pEfsUserInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine obtains all the interesting information about the user
|
|
that we're going to need later.
|
|
|
|
Arguments:
|
|
|
|
pEfsUserInfo - Supplies a pointer to an EfsUserInfo structure which
|
|
will be filled in.
|
|
|
|
Return Value:
|
|
|
|
return-value - Description of conditions needed to return value. - or -
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
BOOLEAN fReturn = FALSE;
|
|
DWORD rc = ERROR_SUCCESS;
|
|
|
|
memset( pEfsUserInfo, 0, sizeof( EFS_USER_INFO ));
|
|
|
|
Status = EfspGetUserName(pEfsUserInfo);
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
|
|
EfspIsDomainUser( pEfsUserInfo->lpDomainName, &pEfsUserInfo->bDomainAccount );
|
|
|
|
EfspIsSystem( pEfsUserInfo, &pEfsUserInfo->bIsSystem );
|
|
if (pEfsUserInfo->bIsSystem) {
|
|
pEfsUserInfo->bDomainAccount = FALSE;
|
|
}
|
|
|
|
fReturn = TRUE;
|
|
|
|
pEfsUserInfo->pUserCache = EfspGetUserCache( pEfsUserInfo );
|
|
|
|
|
|
} else {
|
|
|
|
rc = RtlNtStatusToDosError( Status );
|
|
}
|
|
|
|
SetLastError( rc );
|
|
|
|
return( fReturn );
|
|
}
|
|
|
|
VOID
|
|
EfspFreeUserInfo(
|
|
IN PEFS_USER_INFO pEfsUserInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Frees the memory allocated by EfspGetUserInfo(). Does
|
|
not free the passed structure.
|
|
|
|
Arguments:
|
|
|
|
pEfsUserInfo - Supplies a pointer to the structure to be
|
|
de-allocated.
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
if (pEfsUserInfo->lpUserName) {
|
|
LsapFreeLsaHeap( pEfsUserInfo->lpUserName );
|
|
}
|
|
|
|
if (pEfsUserInfo->lpDomainName) {
|
|
LsapFreeLsaHeap( pEfsUserInfo->lpDomainName );
|
|
}
|
|
|
|
if (pEfsUserInfo->lpProfilePath) {
|
|
LsapFreeLsaHeap( pEfsUserInfo->lpProfilePath );
|
|
}
|
|
|
|
if (pEfsUserInfo->pTokenUser) {
|
|
LsapFreeLsaHeap( pEfsUserInfo->pTokenUser );
|
|
}
|
|
|
|
if (pEfsUserInfo->lpUserSid) {
|
|
UNICODE_STRING Dummy;
|
|
Dummy.Buffer = pEfsUserInfo->lpUserSid;
|
|
RtlFreeUnicodeString(&Dummy);
|
|
}
|
|
|
|
if (pEfsUserInfo->lpKeyPath) {
|
|
LsapFreeLsaHeap( pEfsUserInfo->lpKeyPath );
|
|
}
|
|
|
|
if (pEfsUserInfo->pUserCache) {
|
|
/*
|
|
#if DBG
|
|
|
|
DbgPrint("Cache Ref Count Before Release = %ld\n",pEfsUserInfo->pUserCache->UseRefCount);
|
|
|
|
#endif
|
|
*/
|
|
|
|
EfspReleaseUserCache(pEfsUserInfo->pUserCache);
|
|
/*
|
|
#if DBG
|
|
|
|
DbgPrint("Cache Ref Count After Release = %ld\n",pEfsUserInfo->pUserCache->UseRefCount);
|
|
|
|
#endif
|
|
*/
|
|
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
BOOL
|
|
EfspLoadUserProfile(
|
|
IN PEFS_USER_INFO pEfsUserInfo,
|
|
OUT PHANDLE hToken,
|
|
OUT PHANDLE hProfile
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine attempts to determine if the user's profile is loaded,
|
|
and if it is not, loads it.
|
|
|
|
Callers are expected to call EfspUnloadUserProfile() during their cleanup.
|
|
|
|
Arguments:
|
|
|
|
pEfsUserInfo - Supplies useful information about the current user.
|
|
|
|
hToken - Returns a handle to the user's token.
|
|
|
|
hProfile - Returns a handle to the user's profile.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the profile is already loaded or if this routine loads it successfully,
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD rc = ERROR_SUCCESS;
|
|
|
|
BOOLEAN b = FALSE;
|
|
BOOL fReturn = FALSE;
|
|
|
|
LPWSTR lpServerName = NULL;
|
|
PUSER_INFO_3 lpUserInfo = NULL;
|
|
LPWSTR lpLocalProfilePath = NULL;
|
|
|
|
BOOLEAN DomainUser = pEfsUserInfo->bDomainAccount;
|
|
BOOLEAN IsSystem = pEfsUserInfo->bIsSystem;
|
|
LPWSTR lpDomainName = pEfsUserInfo->lpDomainName;
|
|
LPWSTR lpProfilePath = pEfsUserInfo->lpProfilePath;
|
|
LPWSTR lpUserName = pEfsUserInfo->lpUserName;
|
|
LPWSTR SidString = pEfsUserInfo->lpUserSid;
|
|
|
|
NTSTATUS Status;
|
|
|
|
PDOMAIN_CONTROLLER_INFO DomainControllerInfo = NULL;
|
|
|
|
*hToken = NULL;
|
|
*hProfile = NULL;
|
|
|
|
if (IsSystem) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (pEfsUserInfo->InterActiveUser == USER_INTERACTIVE) {
|
|
return TRUE;
|
|
}
|
|
|
|
Status = NtOpenThreadToken(
|
|
NtCurrentThread(),
|
|
TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_DUPLICATE,
|
|
TRUE, // OpenAsSelf
|
|
hToken
|
|
);
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
|
|
LONG lRet;
|
|
HKEY phKeyCurrentUser;
|
|
|
|
lRet = RegOpenKeyExW(
|
|
HKEY_USERS,
|
|
SidString,
|
|
0, // dwOptions
|
|
MAXIMUM_ALLOWED,
|
|
&phKeyCurrentUser
|
|
);
|
|
|
|
if (ERROR_SUCCESS == lRet) {
|
|
|
|
//
|
|
// The profile is loaded. Ref it so it doesn't disappear.
|
|
//
|
|
|
|
PROFILEINFO pi;
|
|
|
|
ZeroMemory (&pi, sizeof(pi));
|
|
pi.dwSize = sizeof(pi);
|
|
pi.lpUserName = lpUserName;
|
|
pi.dwFlags = PI_LITELOAD;
|
|
|
|
//
|
|
// Cannot be impersonating when loading the profile
|
|
//
|
|
|
|
RevertToSelf();
|
|
|
|
__try {
|
|
|
|
fReturn = LoadUserProfile (*hToken, &pi);
|
|
|
|
} __except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
|
|
fReturn = FALSE;
|
|
SetLastError( GetExceptionCode() );
|
|
}
|
|
|
|
if (!fReturn) {
|
|
|
|
rc = GetLastError();
|
|
|
|
} else {
|
|
|
|
*hProfile = pi.hProfile;
|
|
}
|
|
|
|
//
|
|
// Start impersonating again
|
|
//
|
|
|
|
Status = NtSetInformationThread(
|
|
NtCurrentThread(),
|
|
ThreadImpersonationToken,
|
|
(PVOID) hToken,
|
|
sizeof(HANDLE)
|
|
);
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
//
|
|
// Well, if we can't start impersonating again,
|
|
// we're not going to be able to continue the operation.
|
|
// So unload the profile and fail this whole thing.
|
|
//
|
|
|
|
if (fReturn) {
|
|
(VOID) UnloadUserProfile (*hToken, *hProfile);
|
|
}
|
|
|
|
fReturn = FALSE;
|
|
DebugLog((DEB_ERROR, "EfspLoadUserProfile: Unloading profile, NtSetInformationThread returned %x\n" ,Status ));
|
|
|
|
rc = RtlNtStatusToDosError( Status );
|
|
}
|
|
|
|
RegCloseKey( phKeyCurrentUser );
|
|
|
|
} else {
|
|
|
|
//
|
|
// The profile is not loaded. Load it.
|
|
//
|
|
|
|
if (IsSystem) {
|
|
|
|
lpLocalProfilePath = NULL;
|
|
DebugLog((DEB_TRACE_EFS, "Attempting to open stream from System context\n" ));
|
|
|
|
} else {
|
|
|
|
if (lpProfilePath != NULL) {
|
|
|
|
//
|
|
// We got the profile path from the logon information.
|
|
//
|
|
|
|
DebugLog((DEB_TRACE_EFS, "Got profile path %ws from logon information", lpProfilePath ));
|
|
|
|
//
|
|
// Do this up here so we can have common code below.
|
|
//
|
|
|
|
// RevertToSelf();
|
|
|
|
lpLocalProfilePath = lpProfilePath;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We didn't get a profile path from the logon information,
|
|
// do it the slow way.
|
|
//
|
|
|
|
DebugLog((DEB_TRACE_EFS, "Attempting to compute profile information\n" ));
|
|
|
|
BOOLEAN fGotServerName = TRUE;
|
|
|
|
if (DomainUser) {
|
|
|
|
//
|
|
// Determine the name of the DC for this domain
|
|
//
|
|
|
|
rc = DsGetDcName(
|
|
NULL,
|
|
lpDomainName,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&DomainControllerInfo
|
|
);
|
|
|
|
if (ERROR_SUCCESS == rc) {
|
|
|
|
lpServerName = DomainControllerInfo->DomainControllerName;
|
|
|
|
} else {
|
|
|
|
DebugLog((DEB_ERROR, "Failed to obtain DC Name from DsGetDcName, error = %d\n" ,rc ));
|
|
fGotServerName = FALSE;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Local user, query the local machine
|
|
//
|
|
|
|
lpServerName = NULL;
|
|
}
|
|
|
|
if (fGotServerName) {
|
|
|
|
//
|
|
// Need to do this so that NetUserGetInfo will work.
|
|
// If we don't, the server may fail trying to impersonate us,
|
|
// and then the API will fail. If we revert, then it will only
|
|
// fail if the machine has been denied access.
|
|
//
|
|
|
|
// RevertToSelf();
|
|
|
|
rc = NetUserGetInfo( lpServerName, lpUserName, 3, (LPBYTE *)&lpUserInfo );
|
|
|
|
if (ERROR_SUCCESS == rc) {
|
|
|
|
lpLocalProfilePath = lpUserInfo->usri3_profile;
|
|
|
|
} else {
|
|
|
|
DebugLog((DEB_ERROR, "NetUserGetInfo failed, error = %d\n" ,rc ));
|
|
|
|
//
|
|
// Start impersonating again
|
|
//
|
|
|
|
/* No need to do this. We are still impersonating.
|
|
|
|
Status = NtSetInformationThread(
|
|
NtCurrentThread(),
|
|
ThreadImpersonationToken,
|
|
(PVOID) hToken,
|
|
sizeof(HANDLE)
|
|
);
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
fReturn = FALSE;
|
|
DebugLog((DEB_ERROR, "EfspLoadUserProfile: NtSetInformationThread returned %x\n" ,Status ));
|
|
|
|
rc = RtlNtStatusToDosError( Status );
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make sure we revert before calling LoadUserProfile
|
|
//
|
|
|
|
RevertToSelf();
|
|
|
|
//
|
|
// We have a profile path. Note that it may be NULL.
|
|
//
|
|
|
|
DebugLog((DEB_TRACE_EFS, "Loading profile path %ws\n" , (lpLocalProfilePath == NULL ? L"NULL" : lpLocalProfilePath)));
|
|
|
|
PROFILEINFO pi;
|
|
|
|
ZeroMemory (&pi, sizeof(pi));
|
|
pi.dwSize = sizeof(pi);
|
|
pi.lpUserName = lpUserName;
|
|
pi.lpProfilePath = lpLocalProfilePath;
|
|
pi.dwFlags = PI_LITELOAD;
|
|
|
|
__try {
|
|
|
|
fReturn = LoadUserProfile (*hToken, &pi);
|
|
|
|
} __except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
|
|
fReturn = FALSE;
|
|
SetLastError( GetExceptionCode() );
|
|
}
|
|
|
|
if (!fReturn) {
|
|
|
|
rc = GetLastError();
|
|
DebugLog((DEB_ERROR, "LoadUserProfile failed, error = %d\n" ,rc ));
|
|
|
|
} else {
|
|
|
|
*hProfile = pi.hProfile;
|
|
}
|
|
|
|
//
|
|
// Start impersonating again, at least until LoadUserProfile
|
|
// stops turning off impersonation.
|
|
//
|
|
|
|
Status = NtSetInformationThread(
|
|
NtCurrentThread(),
|
|
ThreadImpersonationToken,
|
|
(PVOID) hToken,
|
|
sizeof(HANDLE)
|
|
);
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
fReturn = FALSE;
|
|
DebugLog((DEB_ERROR, "EfspLoadUserProfile: NtSetInformationThread returned %x\n" ,Status ));
|
|
|
|
rc = RtlNtStatusToDosError( Status );
|
|
}
|
|
|
|
if (lpUserInfo != NULL) {
|
|
NetApiBufferFree( lpUserInfo );
|
|
}
|
|
}
|
|
|
|
if (lpServerName) {
|
|
NetApiBufferFree( DomainControllerInfo );
|
|
}
|
|
|
|
if (!fReturn) {
|
|
|
|
//
|
|
// We did not succeed for some reason.
|
|
// Clean up what we were going to return.
|
|
//
|
|
|
|
NtClose( *hToken );
|
|
}
|
|
|
|
} else {
|
|
|
|
SetLastError( RtlNtStatusToDosError( Status ));
|
|
}
|
|
|
|
SetLastError( rc );
|
|
|
|
return( fReturn );
|
|
}
|
|
|
|
DWORD
|
|
GenerateDRF(
|
|
IN PEFS_KEY Fek,
|
|
OUT PENCRYPTED_KEYS *pNewDRF,
|
|
OUT DWORD *cbDRF
|
|
)
|
|
{
|
|
DWORD rc;
|
|
DWORD DRFKeyCount;
|
|
DWORD DRFStatus;
|
|
PBYTE * DRFPublicKeys;
|
|
DWORD * DRFPublicKeyLengths;
|
|
PBYTE * DRFCertHashes;
|
|
DWORD * DRFCertHashLengths;
|
|
LPWSTR * DRFDisplayInformation;
|
|
PSID * pDRFSid;
|
|
|
|
*pNewDRF = NULL;
|
|
*cbDRF = 0;
|
|
|
|
rc = GetRecoveryData(
|
|
&DRFKeyCount,
|
|
&DRFStatus,
|
|
&DRFPublicKeys,
|
|
&DRFPublicKeyLengths,
|
|
&DRFCertHashes,
|
|
&DRFCertHashLengths,
|
|
&DRFDisplayInformation,
|
|
&pDRFSid
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
if (DRFKeyCount > 0) {
|
|
|
|
rc = ConstructKeyRing(
|
|
Fek,
|
|
DRFKeyCount,
|
|
NULL, // No key name information for recovery agents
|
|
NULL,
|
|
DRFPublicKeys,
|
|
DRFPublicKeyLengths,
|
|
DRFCertHashes,
|
|
DRFCertHashLengths,
|
|
DRFDisplayInformation,
|
|
pDRFSid,
|
|
FALSE,
|
|
pNewDRF,
|
|
cbDRF
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// No DRF will be returned
|
|
//
|
|
|
|
if (DRFStatus < RECOVERY_POLICY_OK) {
|
|
|
|
//
|
|
// EFS will go ahead with encryption without DRF
|
|
//
|
|
|
|
rc = ERROR_SUCCESS;
|
|
|
|
|
|
} else {
|
|
|
|
rc = ERROR_BAD_RECOVERY_POLICY;
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
BOOLEAN
|
|
EqualEncryptedKeys(
|
|
IN PENCRYPTED_KEYS SrcKeys,
|
|
IN PENCRYPTED_KEYS DstKeys,
|
|
IN DWORD cbDstKeys
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine compares two encrypted key arrays.
|
|
|
|
Arguments:
|
|
|
|
SrcKeys - Source key arrays.
|
|
|
|
DstKeys - Destination key arrays.
|
|
|
|
cbDstKeys - Destination key array size.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the profile is already loaded or if this routine loads it successfully,
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
DWORD cbSrcKeys = 0;
|
|
ULONG KeyCount = *(ULONG UNALIGNED *) &(SrcKeys->KeyCount);
|
|
PENCRYPTED_KEY pEncryptedKey;
|
|
ULONG keyLength;
|
|
|
|
if (KeyCount != DstKeys->KeyCount ) {
|
|
return FALSE;
|
|
}
|
|
|
|
pEncryptedKey = &(SrcKeys->EncryptedKey[0]);
|
|
|
|
while ( KeyCount > 0 ) {
|
|
keyLength = * (ULONG UNALIGNED *) &(pEncryptedKey->Length);
|
|
cbSrcKeys += keyLength;
|
|
pEncryptedKey = (PENCRYPTED_KEY)( ((PBYTE)pEncryptedKey) + keyLength );
|
|
KeyCount--;
|
|
}
|
|
|
|
cbSrcKeys += (sizeof ( ENCRYPTED_KEYS ) - sizeof( ENCRYPTED_KEY ));
|
|
|
|
if ( cbSrcKeys != cbDstKeys ) {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return RtlEqualMemory( SrcKeys, DstKeys, cbDstKeys);
|
|
|
|
}
|
|
|
|
DWORD
|
|
EfsGetCertNameFromCertContext(
|
|
PCCERT_CONTEXT CertContext,
|
|
LPWSTR * UserDispName
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Get the user name from the certificate
|
|
Arguments:
|
|
CertContext -- Cert Context
|
|
UserCertName -- User Common Name ( Caller is responsible to delete this memory )
|
|
Return Value:
|
|
ERROR_SUCCESS if succeed.
|
|
If No Name found. "USER_UNKNOWN is returned".
|
|
--*/
|
|
|
|
{
|
|
DWORD NameLength;
|
|
DWORD UserNameBufLen = 0;
|
|
DWORD BlobLen = 0;
|
|
PCERT_EXTENSION AlterNameExt = NULL;
|
|
BOOL b;
|
|
LPWSTR DNSName = NULL;
|
|
LPWSTR UPNName = NULL;
|
|
LPWSTR CommonName = NULL;
|
|
DWORD rc = ERROR_SUCCESS;
|
|
|
|
if ( NULL == UserDispName ){
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
*UserDispName = NULL;
|
|
|
|
AlterNameExt = CertFindExtension(
|
|
szOID_SUBJECT_ALT_NAME2,
|
|
CertContext->pCertInfo->cExtension,
|
|
CertContext->pCertInfo->rgExtension
|
|
);
|
|
|
|
if (AlterNameExt){
|
|
|
|
//
|
|
// Find the alternative name
|
|
//
|
|
|
|
b = CryptDecodeObject(
|
|
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
szOID_SUBJECT_ALT_NAME ,
|
|
AlterNameExt->Value.pbData,
|
|
AlterNameExt->Value.cbData,
|
|
0,
|
|
NULL,
|
|
&BlobLen
|
|
);
|
|
if (b){
|
|
|
|
//
|
|
// Let's decode it
|
|
//
|
|
|
|
CERT_ALT_NAME_INFO *AltNameInfo = NULL;
|
|
|
|
AltNameInfo = (CERT_ALT_NAME_INFO *) LsapAllocateLsaHeap( BlobLen );
|
|
if (AltNameInfo){
|
|
|
|
b = CryptDecodeObject(
|
|
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
szOID_SUBJECT_ALT_NAME,
|
|
AlterNameExt->Value.pbData,
|
|
AlterNameExt->Value.cbData,
|
|
0,
|
|
AltNameInfo,
|
|
&BlobLen
|
|
);
|
|
if (b){
|
|
|
|
//
|
|
// Now search for the UPN, SPN, DNS, EFS name
|
|
//
|
|
|
|
DWORD cAltEntry = AltNameInfo->cAltEntry;
|
|
DWORD ii = 0;
|
|
|
|
while (ii < cAltEntry){
|
|
if ((AltNameInfo->rgAltEntry[ii].dwAltNameChoice == CERT_ALT_NAME_OTHER_NAME ) &&
|
|
!strcmp(szOID_NT_PRINCIPAL_NAME, AltNameInfo->rgAltEntry[ii].pOtherName->pszObjId)
|
|
){
|
|
|
|
//
|
|
// We found the UPN name
|
|
//
|
|
|
|
CERT_NAME_VALUE* CertUPNName = NULL;
|
|
|
|
b = CryptDecodeObject(
|
|
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
X509_UNICODE_ANY_STRING,
|
|
AltNameInfo->rgAltEntry[ii].pOtherName->Value.pbData,
|
|
AltNameInfo->rgAltEntry[ii].pOtherName->Value.cbData,
|
|
0,
|
|
NULL,
|
|
&BlobLen
|
|
);
|
|
if (b){
|
|
|
|
CertUPNName = (CERT_NAME_VALUE *) LsapAllocateLsaHeap(BlobLen);
|
|
if (CertUPNName){
|
|
b = CryptDecodeObject(
|
|
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
X509_UNICODE_ANY_STRING,
|
|
AltNameInfo->rgAltEntry[ii].pOtherName->Value.pbData,
|
|
AltNameInfo->rgAltEntry[ii].pOtherName->Value.cbData,
|
|
0,
|
|
CertUPNName,
|
|
&BlobLen
|
|
);
|
|
if (b){
|
|
UPNName = (LPWSTR)LsapAllocateLsaHeap( CertUPNName->Value.cbData + sizeof(WCHAR) );
|
|
if (UPNName){
|
|
wcscpy(UPNName, (LPCWSTR) CertUPNName->Value.pbData);
|
|
}
|
|
}
|
|
LsapFreeLsaHeap(CertUPNName);
|
|
if (UPNName){
|
|
|
|
//
|
|
// Got the UPN name. Stop searching.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
//
|
|
// Check for other alternative name
|
|
//
|
|
|
|
if (AltNameInfo->rgAltEntry[ii].dwAltNameChoice == CERT_ALT_NAME_DNS_NAME){
|
|
DNSName = AltNameInfo->rgAltEntry[ii].pwszDNSName;
|
|
}
|
|
|
|
}
|
|
|
|
ii++;
|
|
|
|
}
|
|
|
|
if ( NULL == UPNName ){
|
|
|
|
//
|
|
// No UPN name, let's get the other option
|
|
//
|
|
|
|
if (DNSName){
|
|
UPNName = (LPTSTR) LsapAllocateLsaHeap ( sizeof(WCHAR) * (wcslen( DNSName ) + 1));
|
|
if (UPNName){
|
|
wcscpy(UPNName, DNSName);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
LsapFreeLsaHeap(AltNameInfo);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
NameLength = CertGetNameString(
|
|
CertContext,
|
|
CERT_NAME_SIMPLE_DISPLAY_TYPE,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
if ( NameLength > 1){
|
|
|
|
//
|
|
// The display name exist. Go get the display name.
|
|
//
|
|
|
|
CommonName = (LPWSTR) LsapAllocateLsaHeap( sizeof(WCHAR) * NameLength);
|
|
if ( NULL == CommonName ){
|
|
if (UPNName){
|
|
LsapFreeLsaHeap( UPNName );
|
|
}
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
UserNameBufLen = NameLength;
|
|
NameLength = CertGetNameString(
|
|
CertContext,
|
|
CERT_NAME_SIMPLE_DISPLAY_TYPE,
|
|
0,
|
|
NULL,
|
|
CommonName,
|
|
UserNameBufLen
|
|
);
|
|
|
|
ASSERT (NameLength == UserNameBufLen);
|
|
|
|
}
|
|
|
|
|
|
if (CommonName || UPNName){
|
|
|
|
NameLength = 3;
|
|
if (CommonName){
|
|
NameLength += wcslen(CommonName);
|
|
}
|
|
if (UPNName){
|
|
NameLength += wcslen(UPNName);
|
|
}
|
|
|
|
|
|
*UserDispName = (LPWSTR)LsapAllocateLsaHeap(sizeof(WCHAR) * NameLength);
|
|
if (*UserDispName) {
|
|
if (CommonName){
|
|
wcscpy(*UserDispName, CommonName);
|
|
if (UPNName){
|
|
wcscat(*UserDispName, L"(");
|
|
wcscat(*UserDispName, UPNName);
|
|
wcscat(*UserDispName, L")");
|
|
}
|
|
} else {
|
|
wcscpy(*UserDispName, UPNName);
|
|
}
|
|
} else {
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
if (CommonName){
|
|
LsapFreeLsaHeap( CommonName );
|
|
}
|
|
if (UPNName){
|
|
LsapFreeLsaHeap( UPNName );
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
return DISP_E_UNKNOWNNAME;
|
|
|
|
}
|
|
|
|
|
|
DWORD
|
|
EfsAddCertToTrustStoreStore(
|
|
IN PCCERT_CONTEXT pCert,
|
|
OUT DWORD *ImpersonationError
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine adds the cert to the LM Trusted store.
|
|
|
|
Arguments:
|
|
|
|
pCert -- The cert to be added.
|
|
ImpersonationError -- Error indicate that we could impersonate after revert to self. This should not be the case.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
NTSTATUS Status;
|
|
HANDLE hToken;
|
|
DWORD errCode = ERROR_SUCCESS;
|
|
HCERTSTORE localStore;
|
|
|
|
*ImpersonationError = 0;
|
|
Status = NtOpenThreadToken(
|
|
NtCurrentThread(),
|
|
TOKEN_QUERY | TOKEN_IMPERSONATE,
|
|
TRUE, // OpenAsSelf
|
|
&hToken
|
|
);
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
|
|
RevertToSelf();
|
|
|
|
localStore = CertOpenStore(
|
|
CERT_STORE_PROV_SYSTEM_W,
|
|
0, // dwEncodingType
|
|
0, // hCryptProv,
|
|
CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_MAXIMUM_ALLOWED_FLAG,
|
|
TRUSTEDPEOPLE
|
|
);
|
|
if (localStore) {
|
|
LPWSTR crntUserName;
|
|
LPWSTR userName;
|
|
PCCERT_CONTEXT userCert = NULL;
|
|
CERT_ENHKEY_USAGE certEnhKeyUsage;
|
|
LPSTR lpstr = szOID_EFS_CRYPTO;
|
|
|
|
certEnhKeyUsage.cUsageIdentifier = 1;
|
|
certEnhKeyUsage.rgpszUsageIdentifier = &lpstr;
|
|
|
|
errCode = EfsGetCertNameFromCertContext(
|
|
pCert,
|
|
&crntUserName
|
|
);
|
|
|
|
if (crntUserName) {
|
|
|
|
//
|
|
// Let's enumerate the certs in the store to see if we need to remove the old similar
|
|
// EFS cert.
|
|
//
|
|
|
|
do {
|
|
|
|
userCert = CertFindCertificateInStore(
|
|
localStore,
|
|
X509_ASN_ENCODING,
|
|
0, //CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG,
|
|
CERT_FIND_ENHKEY_USAGE,
|
|
&certEnhKeyUsage,
|
|
userCert
|
|
);
|
|
if (userCert) {
|
|
|
|
EfsGetCertNameFromCertContext(
|
|
userCert,
|
|
&userName
|
|
);
|
|
if (userName) {
|
|
|
|
if (!wcscmp(userName, crntUserName) ) {
|
|
|
|
//
|
|
// Find the name match. Remove it.
|
|
//
|
|
|
|
PCCERT_CONTEXT tmpCert;
|
|
|
|
tmpCert = CertDuplicateCertificateContext(userCert);
|
|
if (tmpCert) {
|
|
CertDeleteCertificateFromStore(tmpCert);
|
|
}
|
|
|
|
}
|
|
|
|
LsapFreeLsaHeap( userName );
|
|
|
|
}
|
|
}
|
|
|
|
} while (userCert);
|
|
|
|
if(!CertAddCertificateContextToStore(
|
|
localStore,
|
|
pCert,
|
|
CERT_STORE_ADD_NEW,
|
|
NULL) ) {
|
|
|
|
errCode = GetLastError();
|
|
|
|
}
|
|
|
|
LsapFreeLsaHeap( crntUserName );
|
|
|
|
} else {
|
|
errCode = GetLastError();
|
|
}
|
|
|
|
CertCloseStore( localStore, 0 );
|
|
}
|
|
|
|
Status = NtSetInformationThread(
|
|
NtCurrentThread(),
|
|
ThreadImpersonationToken,
|
|
(PVOID) &hToken,
|
|
sizeof(HANDLE)
|
|
);
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
ASSERT(FALSE);
|
|
*ImpersonationError = 1;
|
|
errCode = RtlNtStatusToDosError( Status );
|
|
|
|
}
|
|
|
|
} else {
|
|
errCode = RtlNtStatusToDosError( Status );
|
|
}
|
|
|
|
return errCode;
|
|
|
|
}
|
|
|
|
DWORD
|
|
EfsAlignBlock(
|
|
IN PVOID InPointer,
|
|
OUT PVOID *OutPointer,
|
|
OUT BOOLEAN *NewMemory
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will align the structure with the first ULONG as the length of the structure
|
|
so that we don't get alignment faults.
|
|
|
|
Arguments:
|
|
|
|
InPointer -- Original Block
|
|
OutPointer -- Aligned Block
|
|
NewMemory -- If new memory block allocated
|
|
|
|
Return Value:
|
|
|
|
Win32 error code
|
|
|
|
--*/
|
|
{
|
|
|
|
if ( ((INT_PTR) InPointer & 0x03) == 0) {
|
|
*OutPointer = InPointer;
|
|
*NewMemory = FALSE;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
ULONG length;
|
|
DWORD result=ERROR_SUCCESS;
|
|
|
|
RtlCopyMemory(&length, InPointer, sizeof (ULONG));
|
|
*OutPointer = (PENCRYPTED_KEY)LsapAllocateLsaHeap(length);
|
|
if (*OutPointer) {
|
|
|
|
RtlCopyMemory(*OutPointer, InPointer, length);
|
|
*NewMemory = TRUE;
|
|
} else {
|
|
*NewMemory = FALSE;
|
|
result = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
return result;
|
|
}
|