/*++ Copyright (c) 1998 Microsoft Corporation Module Name: recovery.cpp Abstract: This module implements the callout from winlogon to set recovery policy. Author: Robert Reichel (RobertRe) Revision History: --*/ // // Turn off lean and mean so we get wincrypt.h and winefs.h included // #undef WIN32_LEAN_AND_MEAN #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sclgntfy.hxx" #define YEARCOUNT (LONGLONG) 10000000*3600*24*365 // One Year's tick count typedef BOOL (WINAPI *PFREFRESHPOLICY)(BOOL); extern HINSTANCE g_hDllInstance; HANDLE hEventLog = NULL; TCHAR EventSourceName[] = TEXT("SclgNtfy"); GUID guidExtension = { 0xb1be8d72, 0x6eac, 0x11d2, {0xa4, 0xea, 0x00, 0xc0, 0x4f, 0x79, 0xf8, 0x3a }}; GUID guidRegExt = REGISTRY_EXTENSION_GUID; GUID guidSnapin = {0x53D6AB1B,0x2488,0x11d1,{0xA2,0x8C,0x00,0xC0,0x4F,0xB9,0x4F,0x17}}; // CLSID_CertificateManager // // Event handle // PTOKEN_USER GetTokenUser( HANDLE TokenHandle ); BOOLEAN CreateSelfSignedRecoveryCertificate( IN BOOL bIsDC, OUT PCCERT_CONTEXT * pCertContext ); BOOL EncodeAndAlloc( DWORD dwEncodingType, LPCSTR lpszStructType, const void * pvStructInfo, PBYTE * pbEncoded, PDWORD pcbEncoded ); LPWSTR MakeDNName( VOID ); DWORD CreatePublicKeyInformationCertificate( IN PSID pUserSid OPTIONAL, PBYTE pbCert, DWORD cbCert, OUT PEFS_PUBLIC_KEY_INFO * PublicKeyInformation ); DWORD GetDefaultRecoveryPolicy( IN HANDLE hToken, IN BOOL bIsDC, OUT PUCHAR *pRecoveryPolicyBlob, OUT ULONG *PolicySize, OUT PCCERT_CONTEXT *ppCertContext ); HRESULT CreateLocalMachinePolicy( IN PWLX_NOTIFICATION_INFO pInfo, IN BOOL bIsDC, OUT PUCHAR *pEfsBlob, OUT ULONG *pEfsSize, OUT PCCERT_CONTEXT *ppCertContext ); typedef DWORD (WINAPI *PFNDSGETDCNAME)(LPCTSTR, LPCTSTR, GUID *, LPCTSTR, ULONG, PDOMAIN_CONTROLLER_INFO *); HRESULT CreateEFSDefaultPolicy( IN HANDLE hToken, IN PUCHAR *pEfsBlob, IN DWORD *pEfsSize, IN PCCERT_CONTEXT *ppCertContext, IN LPTSTR DomainName ); DWORD MyLdapOpen( OUT PLDAP *pLdap ); DWORD MyGetDsObjectRoot( IN PLDAP pLdap, OUT PWSTR *pDsRootName ); HRESULT CreateGroupPolicyObjectInDomain( IN HANDLE hToken, IN PWSTR DomainNCName, IN PWSTR GPObjectName, IN PUCHAR EfsBlob, IN ULONG EfsSize, IN PCCERT_CONTEXT pCertContext, OUT LPGROUPPOLICYOBJECT *ppObject ); DWORD CheckExistingEFSPolicy( IN PLDAP phLdap, IN LPTSTR DomainNCName, OUT BOOL *bExist ); DWORD MyLdapClose( IN PLDAP *pLdap ); LPWSTR GetCertDisplayInformation( IN PCCERT_CONTEXT pCertContext ); BOOLEAN IsSelfSignedPolicyExpired( IN PRECOVERY_POLICY_1_1 RecoveryPolicy OPTIONAL ); #define EFS_NOTIFY_PATH TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\Notify\\EFS") extern "C" BOOL EfsRecInit( IN PVOID hmod, IN ULONG Reason, IN PCONTEXT Context ) { return( TRUE ); } ///////////////////////////////////////////////////////////////////////////// // DllRegisterServer - Adds entries to the system registry STDAPI DllRegisterServerEFS(void) { HKEY hKey; LONG lResult; DWORD dwDisp; lResult = RegCreateKeyEx( HKEY_LOCAL_MACHINE, EFS_NOTIFY_PATH, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, &dwDisp ); if (lResult != ERROR_SUCCESS) { return lResult; } RegSetValueEx( hKey, TEXT("Logon"), 0, REG_SZ, (LPBYTE)TEXT("WLEventLogon"), (lstrlen(TEXT("WLEventLogon")) + 1) * sizeof(TCHAR) ); RegSetValueEx( hKey, TEXT("DllName"), 0, REG_EXPAND_SZ, (LPBYTE)TEXT("sclgntfy.dll"), (lstrlen(TEXT("sclgntfy.dll")) + 1) * sizeof(TCHAR) ); // // increase the wait limit to 10 minutes // DWORD dwValue = 120; RegSetValueEx( hKey, TEXT("MaxWait"), 0, REG_DWORD, (LPBYTE)&dwValue, sizeof(DWORD) ); RegCloseKey (hKey); return S_OK; } ///////////////////////////////////////////////////////////////////////////// // DllUnregisterServer - Removes entries from the system registry STDAPI DllUnregisterServerEFS(void) { RegDeleteKey (HKEY_LOCAL_MACHINE, EFS_NOTIFY_PATH); return S_OK; } DWORD WINAPI EFSRecPolicyPostProcess( IN LPVOID Param ) /*++ Routine Description: Enumerate the volumes and do the possible recovery jobs caused by power outage or crash during encryption or decryption. Arguments: Param -- Standard parameter for thread. Not used. Return Value: Operation result. --*/ { PEFS_POLICY_POST_PROCESS pPcType = (PEFS_POLICY_POST_PROCESS) Param; LPWSTR objName = NULL; LPWSTR popUpMsg = NULL; LPWSTR popUpTitle = NULL; DWORD dRet; DWORD nSize = MAX_COMPUTERNAME_LENGTH + 1; if ( pPcType == NULL ) { return(ERROR_INVALID_PARAMETER); } if (!SetThreadDesktop( pPcType->ShellWnd )){ return GetLastError(); } if (!(pPcType->PCIsDC)) { // // Get the PC name // BOOL b = GetComputerName ( pPcType->ObjName, &nSize ); if (b) { objName = pPcType->ObjName; } } else { objName = pPcType->ObjName; } dRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, g_hDllInstance, EFS_POLICY_WARNING, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &popUpMsg, 0, (va_list *)&objName ); if (dRet) { dRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY | 80, g_hDllInstance, EFS_POLICY_WARNING_TITLE, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &popUpTitle, 0, NULL ); if (dRet) { MessageBox(NULL, popUpMsg, popUpTitle, MB_OK | MB_ICONINFORMATION); LocalFree( popUpTitle ); } LocalFree( popUpMsg ); } // // free the buffer allocated // LocalFree(pPcType); return 0; } #if 0 DWORD EFSRecPolicyPopup( IN DSROLE_MACHINE_ROLE MachineRole, IN LPTSTR DomainNameFlat OPTIONAL ) { DWORD EFSRecPolicyThreadID; HANDLE EfsWarningThread; PEFS_POLICY_POST_PROCESS PcType = NULL; // // Pop the dialog warning the user that the recovery policy has been created. // PcType = (PEFS_POLICY_POST_PROCESS)LocalAlloc(LPTR, sizeof(EFS_POLICY_POST_PROCESS)); if ( PcType == NULL ) { return(ERROR_NOT_ENOUGH_MEMORY); } if (MachineRole == DsRole_RolePrimaryDomainController || MachineRole == DsRole_RoleBackupDomainController ) { // // The PC is a DC. // PcType->PCIsDC = TRUE; if ( DomainNameFlat) { wcscpy(PcType->ObjName,DomainNameFlat); } else { PcType->ObjName[0] = L'\0'; } } else { PcType->PCIsDC = FALSE; PcType->ObjName[0] = L'\0'; } DWORD rc=ERROR_SUCCESS; PcType->ShellWnd = GetThreadDesktop( GetCurrentThreadId() ); if (PcType->ShellWnd) { EfsWarningThread = CreateThread( NULL, 0, EFSRecPolicyPostProcess, PcType, 0, &EFSRecPolicyThreadID ); if (EfsWarningThread) { CloseHandle(EfsWarningThread); } else { // // free the buffer allocated // LocalFree(PcType); rc = GetLastError(); } } else { // // free the buffer allocated // LocalFree(PcType); rc = GetLastError(); } return rc; } #endif VOID WLEventLogon( PWLX_NOTIFICATION_INFO pInfo ) { // // First, local EFS policy is always created on all products (wks, srv, DC) // DWORD rc; LONG lResult; HKEY hKey; DWORD IsCreated=0; // // even if we were unregistered, without a reboot, we still get this event. // use the EFS... key to see if we were deregistered, and if so, leave. // lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE, EFS_NOTIFY_PATH, 0, MAXIMUM_ALLOWED, &hKey ); if (lResult != ERROR_SUCCESS) { return; } else { // // query if a domain EFS policy was created before // DWORD RegType; DWORD nSize=sizeof(DWORD); RegQueryValueEx( hKey, L"EFSDomainGPOCreated", 0, &RegType, (LPBYTE)&IsCreated, &nSize ); RegCloseKey( hKey ); } if (IsCreated != 0) { // // The DC recovery policy is already set // DllUnregisterServerEFS(); return; } // // check the current logon user. If not a member of administrators, quit // SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; PSID AdminAccountSid; BOOL bIsMemberOfAdmins = FALSE; if ( NT_SUCCESS( RtlAllocateAndInitializeSid( &NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdminAccountSid ) ) ) { HANDLE NewToken; if ( DuplicateToken( pInfo->hToken, SecurityImpersonation, &NewToken )) { if ( FALSE == CheckTokenMembership( NewToken, AdminAccountSid, &bIsMemberOfAdmins ) ) { // // error occured when checking membership, assume it is not a member // bIsMemberOfAdmins = FALSE; } CloseHandle(NewToken); } RtlFreeSid( AdminAccountSid); } if ( bIsMemberOfAdmins == FALSE ) { // // if it's not admin logon, or for some reason token membership can't // be checked, quit ??? // return; } // // impersonate the token // if ( FALSE == ImpersonateLoggedOnUser(pInfo->hToken) ) { return; } // // initialize event log. If failed, will try later for each LogEvent call. // (void) InitializeEvents(); // // check the machine role // PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pDsRole; rc = DsRoleGetPrimaryDomainInformation( NULL, DsRolePrimaryDomainInfoBasic, (PBYTE *)&pDsRole ); if (rc != ERROR_SUCCESS) { // // Try again some other time? Deregister? // This isn't supposed to happen. // LogEvent(STATUS_SEVERITY_ERROR, GPOEVENT_ERROR_DSROLE, IDS_FAILED_GET_DSROLE, GetLastError() ); (void) ShutdownEvents(); RevertToSelf(); return; } DSROLE_MACHINE_ROLE MachineRole = pDsRole->MachineRole; if (MachineRole != DsRole_RolePrimaryDomainController) { // // We don't create EFS recovery policy on non-PDC // (void) ShutdownEvents(); DsRoleFreeMemory( pDsRole ); RevertToSelf(); DllUnregisterServerEFS(); return; } // // create the EFS local policy if not exist // CoInitialize(NULL); HRESULT hr = ERROR_SUCCESS; PUCHAR EfsBlob=NULL; ULONG EfsSize=0; PCCERT_CONTEXT pCertContext=NULL; /* // // check/create EFS policy in the local machine object // hr = CreateLocalMachinePolicy( pInfo, ( MachineRole == DsRole_RolePrimaryDomainController ) ? TRUE : FALSE, &EfsBlob, &EfsSize, &pCertContext ); if ( SUCCEEDED(hr) ) { // // local machine policy has been successfully created // delete the SystemClone flag if it exists (or set it to 0 if it can't be deleted) // lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE, EFS_NOTIFY_PATH, 0, MAXIMUM_ALLOWED, &hKey ); if (lResult == ERROR_SUCCESS) { lResult = RegDeleteValue (hKey, TEXT("SystemCloned") ); if ( lResult == ERROR_ACCESS_DENIED ) { // // can't delete it, now set it to 0 // DWORD dwClone = 0; RegSetValueEx( hKey, TEXT("SystemCloned"), 0, REG_DWORD, (LPBYTE)&dwClone, sizeof(DWORD) ); } RegCloseKey( hKey ); } // // detect the role of this computer and create the default EFS group // policies for domain controllers // // this code is also executed for replicas because if the user logs on // to replica first (before logging onto the PDC), we still want to // have EFS recovery policy created. // // if the replica is a READ ONLY replica (BDC), this call will fail // if ( (MachineRole == DsRole_RolePrimaryDomainController) && ( IsCreated == 0) ) { // // Create the default group policy object and add it to the gPLink list. // if ( EfsBlob == NULL ) { EfsSize = 0; if ( pCertContext ) { // // free it first // CertFreeCertificateContext( pCertContext ); pCertContext = NULL; } } */ // // create both EFS default policy and domain account policy objects. // hr = CreateEFSDefaultPolicy(pInfo->hToken, &EfsBlob, &EfsSize, &pCertContext, pDsRole->DomainNameFlat); if ( SUCCEEDED(hr) ) { // // update the registry value EFSDomainGPOCreated // lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE, EFS_NOTIFY_PATH, 0, MAXIMUM_ALLOWED, &hKey ); if (lResult == ERROR_SUCCESS) { // // set the flag // IsCreated = 1; RegSetValueEx( hKey, L"EFSDomainGPOCreated", 0, REG_DWORD, (LPBYTE)&IsCreated, sizeof(DWORD) ); RegCloseKey( hKey ); } } /* } else { // // the domain GPO is already created, or // // If it's workstation or server (either standalone or member), local policy // (existing or just created) is enough. // // // If it's a backup domain controller (replica), no need to create any // default ( because either the one replicated from the primary domain // or the one created by server setup will take effect ); // // !!! do nothing !!! } */ if ( SUCCEEDED(hr) ) { DllUnregisterServerEFS(); HINSTANCE hUserEnvDll = LoadLibrary(TEXT("userenv.dll")); PFREFRESHPOLICY pfRefreshPolicy=NULL; if ( hUserEnvDll) { pfRefreshPolicy = (PFREFRESHPOLICY)GetProcAddress( hUserEnvDll, "RefreshPolicy"); if ( pfRefreshPolicy ) { // // do not care errors // (void) (*pfRefreshPolicy)(TRUE); } else { LogEvent(STATUS_SEVERITY_WARNING, GPOEVENT_WARNING_NOT_REFRESH, IDS_ERROR_GET_PROC_ADDR, GetLastError() ); } FreeLibrary(hUserEnvDll); } else { LogEvent(STATUS_SEVERITY_WARNING, GPOEVENT_WARNING_NOT_REFRESH, IDS_ERROR_LOAD_USERENV, GetLastError() ); } } // } if (pDsRole) { DsRoleFreeMemory( pDsRole ); } (void) ShutdownEvents(); // // uninitialize OLE // CoUninitialize(); RevertToSelf(); // // clean up buffers // if ( EfsBlob ) { free(EfsBlob); } if ( pCertContext ) { CertFreeCertificateContext( pCertContext ); } } #if 0 HRESULT CreateLocalMachinePolicy( IN PWLX_NOTIFICATION_INFO pInfo, IN BOOL bIsDC, OUT PUCHAR *pEfsBlob, OUT ULONG *pEfsSize, OUT PCCERT_CONTEXT *ppCertContext ) { LPGROUPPOLICYOBJECT pGPO = NULL; HRESULT hr; HKEY hKeyPolicyRoot; HKEY hKey; DWORD rc=ERROR_SUCCESS; // // create Policy Object instance // hr = CoCreateInstance( CLSID_GroupPolicyObject, NULL, CLSCTX_SERVER, IID_IGroupPolicyObject, (PVOID *)&pGPO ); if (SUCCEEDED(hr) && pGPO) { // // open LPO and load policy into registry // if LPO does not exist, create it (under system context now) // hr = pGPO->OpenLocalMachineGPO( TRUE ); if (SUCCEEDED(hr)) { // // get the registry key for the root of machine policy object // hr = pGPO->GetRegistryKey( GPO_SECTION_MACHINE, &hKeyPolicyRoot ); if (SUCCEEDED(hr)) { // // open EFS recovery policy base key in the registry // BOOL fContinue = TRUE; DWORD dwDisposition; if ( (rc = RegCreateKeyEx( hKeyPolicyRoot, CERT_EFSBLOB_REGPATH, 0, TEXT("REG_SZ"), REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwDisposition )) == ERROR_SUCCESS ) { // // check to see if there is EFS blob // if not, create it. // DWORD RegType; DWORD EfsSize; PUCHAR pEfsOldBlob=NULL; BOOL bIsExpired=FALSE; rc = RegQueryValueEx( hKey, CERT_EFSBLOB_VALUE_NAME, 0, &RegType, NULL, &EfsSize ); if ( rc == ERROR_SUCCESS && EfsSize > 0 ) { // // query if this machine is cloned // HKEY hKeyClone; rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE, EFS_NOTIFY_PATH, 0, MAXIMUM_ALLOWED, &hKeyClone ); if (rc == ERROR_SUCCESS) { DWORD nSize=sizeof(DWORD); DWORD dwClone=0; RegQueryValueEx( hKeyClone, L"SystemCloned", 0, &RegType, (LPBYTE)&dwClone, &nSize ); RegCloseKey( hKeyClone ); if ( dwClone > 0 ) { bIsExpired = TRUE; } } if ( !bIsExpired ) { // // EFS recovery policy already exists // query the blob and check if this policy needs to be // upgraded (change expiration date) // pEfsOldBlob = (PUCHAR)LocalAlloc(LPTR, EfsSize+1); if ( pEfsOldBlob ) { rc = RegQueryValueEx( hKey, CERT_EFSBLOB_VALUE_NAME, 0, &RegType, pEfsOldBlob, &EfsSize ); if ( rc == ERROR_SUCCESS ) { // // check if the policy should be recreated. // bIsExpired = IsSelfSignedPolicyExpired((PRECOVERY_POLICY_1_1)pEfsOldBlob); } LocalFree(pEfsOldBlob); pEfsOldBlob = NULL; } else { rc = ERROR_NOT_ENOUGH_MEMORY; } } } if ( rc != ERROR_SUCCESS || EfsSize == 0 || bIsExpired ) { // // the EFSBlob registry value does not exis // this means that local EFS policy should be created // *pEfsSize = 0; rc = GetDefaultRecoveryPolicy(pInfo->hToken, bIsDC, pEfsBlob, pEfsSize, ppCertContext ); if ( ERROR_SUCCESS == rc && *ppCertContext ) { // // add to the cert store of this GPO first // CERT_SYSTEM_STORE_RELOCATE_PARA paraRelocate; paraRelocate.hKeyBase = hKeyPolicyRoot; paraRelocate.pwszSystemStore = L"EFS"; HCERTSTORE hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_W, 0, NULL, CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY | CERT_SYSTEM_STORE_RELOCATE_FLAG, (void *)¶Relocate ); if ( hCertStore ) { if ( bIsExpired ) { // // delete all old certs from the store first // PCCERT_CONTEXT pEnumCertContext = CertEnumCertificatesInStore( hCertStore, NULL ); PCCERT_CONTEXT pCertContextDelete=NULL; while ( pEnumCertContext ) { // // duplicate the cert context // pCertContextDelete = CertDuplicateCertificateContext( pEnumCertContext ); // // enumerate the next certificate context // pEnumCertContext = CertEnumCertificatesInStore( hCertStore, pEnumCertContext ); if ( pCertContextDelete ) { // // delete the cert from store // CertDeleteCertificateFromStore( pCertContextDelete ); // // no need to free the cert context // because it's freed by the previous call (even under error case) // pCertContextDelete = NULL; } } } if ( !CertAddCertificateContextToStore( hCertStore, *ppCertContext, CERT_STORE_ADD_ALWAYS, NULL ) ) { rc = GetLastError(); } // // close the store // CertCloseStore(hCertStore, 0); hCertStore = NULL; } else { rc = GetLastError(); } if ( ERROR_SUCCESS == rc ) { rc = RegSetValueEx( hKey, CERT_EFSBLOB_VALUE_NAME, 0, REG_BINARY, (PBYTE)(*pEfsBlob), *pEfsSize ); if (rc == ERROR_SUCCESS) { hr = pGPO->Save(TRUE, TRUE, &guidExtension, &guidSnapin ); hr = pGPO->Save(TRUE, TRUE, &guidRegExt, &guidSnapin ); if ( FAILED(hr) ) { LogEvent(STATUS_SEVERITY_ERROR, GPOEVENT_ERROR_CREATE_LPO, IDS_ERROR_SAVE_LPO, hr ); } else { /* comment out due to PM request // // popup a message to warn user // EFSRecPolicyPopup( DsRole_RoleStandaloneWorkstation, // this value doesn't matter NULL ); */ } } else { LogEvent(STATUS_SEVERITY_ERROR, GPOEVENT_ERROR_CREATE_LPO, IDS_ERROR_SAVE_EFSBLOB, rc ); } } else { LogEvent(STATUS_SEVERITY_ERROR, GPOEVENT_ERROR_CREATE_LPO, IDS_ERROR_ADD_CERTIFICATE, rc ); } } else { LogEvent(STATUS_SEVERITY_ERROR, GPOEVENT_ERROR_CREATE_LPO, IDS_ERROR_CREATE_EFSBLOB, rc ); } } // // close the registry key // RegCloseKey(hKey); } else { LogEvent(STATUS_SEVERITY_ERROR, GPOEVENT_ERROR_CREATE_LPO, IDS_ERROR_OPEN_EFSKEY, rc ); } if ( rc != ERROR_SUCCESS ) { hr = HRESULT_FROM_WIN32(rc); } RegCloseKey(hKeyPolicyRoot); } else { LogEvent(STATUS_SEVERITY_ERROR, GPOEVENT_ERROR_CREATE_LPO, IDS_ERROR_GET_REGISTRY_KEY, hr ); } } else { LogEvent(STATUS_SEVERITY_ERROR, GPOEVENT_ERROR_CREATE_LPO, IDS_ERROR_OPEN_LPO, hr ); } // // release the policy object instance // pGPO->Release(); } else { LogEvent(STATUS_SEVERITY_ERROR, GPOEVENT_ERROR_CREATE_LPO, IDS_ERROR_CREATE_GPO_INSTANCE, hr ); } return hr; } #endif DWORD GetDefaultRecoveryPolicy( IN HANDLE hToken, IN BOOL bIsDC, OUT PUCHAR *pRecoveryPolicyBlob, OUT ULONG *BlobSize, OUT PCCERT_CONTEXT *ppCertContext ) /*++ Routine Description: This routine creates the default recovery policy for the current system. It does this by auto-generating a recovery key based on the name of the machine. Arguments: hToken - the token of current user pRecoveryPolicyBlob - the output buffer to hold EFSBlob BlobSize - the size of EFSBlob Return Value: --*/ { DWORD rc = ERROR_SUCCESS; PCCERT_CONTEXT pCertContext; *pRecoveryPolicyBlob = NULL; *BlobSize = 0; *ppCertContext = NULL; if (CreateSelfSignedRecoveryCertificate( bIsDC, &pCertContext )) { PEFS_PUBLIC_KEY_INFO PublicKeyInformation; PBYTE pbCert = pCertContext->pbCertEncoded; DWORD cbCert = pCertContext->cbCertEncoded; PTOKEN_USER pTokenUser = GetTokenUser( hToken ); if (pTokenUser) { rc = CreatePublicKeyInformationCertificate( pTokenUser->User.Sid, pbCert, cbCert, &PublicKeyInformation ); if (rc == ERROR_SUCCESS) { // // Compute the total size of the RECOVERY_POLICY_1_1 structure, which // is the size of the structure minus the size of the EFS_PUBLIC_KEY_INFO // plus the size of the thing we just created above. // ULONG RecoveryKeySize = sizeof( RECOVERY_KEY_1_1 ) - sizeof( EFS_PUBLIC_KEY_INFO ) + PublicKeyInformation->Length; ULONG PolicySize = sizeof( RECOVERY_POLICY_1_1 ) + RecoveryKeySize - sizeof( RECOVERY_KEY_1_1 ); // // Allocate the policy block // PRECOVERY_POLICY_1_1 RecoveryPolicy = (PRECOVERY_POLICY_1_1)malloc( PolicySize ); if (RecoveryPolicy != NULL) { RecoveryPolicy->RecoveryPolicyHeader.MajorRevision = EFS_RECOVERY_POLICY_MAJOR_REVISION_1; RecoveryPolicy->RecoveryPolicyHeader.MinorRevision = EFS_RECOVERY_POLICY_MINOR_REVISION_1; RecoveryPolicy->RecoveryPolicyHeader.RecoveryKeyCount = 1; PRECOVERY_KEY_1_1 RecoveryKey = (PRECOVERY_KEY_1_1) &(RecoveryPolicy->RecoveryKeyList[0]); memcpy( &RecoveryKey->PublicKeyInfo, PublicKeyInformation, PublicKeyInformation->Length ); RecoveryKey->TotalLength = sizeof( RECOVERY_KEY_1_1 ) + PublicKeyInformation->Length - sizeof( EFS_PUBLIC_KEY_INFO ); *pRecoveryPolicyBlob = (PUCHAR)RecoveryPolicy; *BlobSize = PolicySize; *ppCertContext = pCertContext; // // will free this outside this function // pCertContext = NULL; } else { rc = ERROR_NOT_ENOUGH_MEMORY; } free( PublicKeyInformation ); } free( pTokenUser ); } else { rc = GetLastError(); } // // free the certificate context if not outputted // if ( pCertContext ) { CertFreeCertificateContext( pCertContext ); } } else { rc = GetLastError(); } return( rc ); } BOOLEAN CreateSelfSignedRecoveryCertificate( IN BOOL bIsDC, OUT PCCERT_CONTEXT * pCertContext ) /*++ Routine Description: This routine sets up and creates a self-signed certificate. Arguments: Return Value: TRUE on success, FALSE on failure. Call GetLastError() for more details. --*/ { BOOLEAN fReturn = FALSE; DWORD rc = ERROR_SUCCESS; LPWSTR lpContainerName = NULL; LPWSTR lpProviderName = NULL; PBYTE pbHash = NULL; LPWSTR lpDisplayInfo = NULL; HCRYPTKEY hKey = NULL; HCRYPTPROV hProv = NULL; *pCertContext = NULL; // // Croft up a key pair // // // Container name // GUID guidContainerName; if ( ERROR_SUCCESS != UuidCreate(&guidContainerName) ) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return(fReturn); } LPWSTR TmpContainerName; if (ERROR_SUCCESS == UuidToStringW(&guidContainerName, (unsigned short **)&lpContainerName )) { // // Copy the container name into LSA heap memory // lpProviderName = MS_DEF_PROV; // // Create the key container // if (CryptAcquireContext(&hProv, lpContainerName, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_NEWKEYSET )) { if (CryptGenKey(hProv, AT_KEYEXCHANGE, RSA1024BIT_KEY | CRYPT_EXPORTABLE, &hKey)) { DWORD NameLength = 64; LPWSTR AgentName = NULL; // // Construct the subject name information // //lpDisplayInfo = MakeDNName(); AgentName = (LPWSTR)malloc(NameLength * sizeof(WCHAR)); if (AgentName){ if (!GetUserName(AgentName, &NameLength)){ free(AgentName); AgentName = (LPWSTR)malloc(NameLength * sizeof(WCHAR)); // // Try again with big buffer // if ( AgentName ){ if (!GetUserName(AgentName, &NameLength)){ rc = GetLastError(); free(AgentName); AgentName = NULL; } } else { rc = ERROR_NOT_ENOUGH_MEMORY; } } } else { rc = ERROR_NOT_ENOUGH_MEMORY; } if (AgentName) { LPCWSTR DNNameTemplate = L"CN=%ws,L=EFS,OU=EFS File Encryption Certificate"; DWORD cbDNName = 0; cbDNName = (wcslen( DNNameTemplate ) + 1) * sizeof( WCHAR ) + (wcslen( AgentName ) + 1) * sizeof( WCHAR ); lpDisplayInfo = (LPWSTR)malloc( cbDNName ); if (lpDisplayInfo) { swprintf( lpDisplayInfo, DNNameTemplate, AgentName ); } else { rc = ERROR_NOT_ENOUGH_MEMORY; } free(AgentName); AgentName = NULL; } if (lpDisplayInfo) { // // Use this piece of code to create the PCERT_NAME_BLOB going into CertCreateSelfSignCertificate() // CERT_NAME_BLOB SubjectName; SubjectName.cbData = 0; if(CertStrToNameW( CRYPT_ASN_ENCODING, lpDisplayInfo, 0, NULL, NULL, &SubjectName.cbData, NULL)) { SubjectName.pbData = (BYTE *) malloc(SubjectName.cbData); if (SubjectName.pbData) { if (CertStrToNameW( CRYPT_ASN_ENCODING, lpDisplayInfo, 0, NULL, SubjectName.pbData, &SubjectName.cbData, NULL) ) { // // Make the enhanced key usage // CERT_ENHKEY_USAGE certEnhKeyUsage; LPSTR lpstr; CERT_EXTENSION certExt; lpstr = szOID_EFS_RECOVERY; certEnhKeyUsage.cUsageIdentifier = 1; certEnhKeyUsage.rgpszUsageIdentifier = &lpstr; // now call CryptEncodeObject to encode the enhanced key usage into the extension struct certExt.Value.cbData = 0; certExt.Value.pbData = NULL; certExt.fCritical = FALSE; certExt.pszObjId = szOID_ENHANCED_KEY_USAGE; // // Encode it // if (EncodeAndAlloc( CRYPT_ASN_ENCODING, X509_ENHANCED_KEY_USAGE, &certEnhKeyUsage, &certExt.Value.pbData, &certExt.Value.cbData )) { // // finally, set up the array of extensions in the certInfo struct // any further extensions need to be added to this array. // CERT_EXTENSIONS certExts; certExts.cExtension = 1; certExts.rgExtension = &certExt; CRYPT_KEY_PROV_INFO KeyProvInfo; memset( &KeyProvInfo, 0, sizeof( CRYPT_KEY_PROV_INFO )); KeyProvInfo.pwszContainerName = lpContainerName; KeyProvInfo.pwszProvName = lpProviderName; KeyProvInfo.dwProvType = PROV_RSA_FULL; KeyProvInfo.dwKeySpec = AT_KEYEXCHANGE; SYSTEMTIME StartTime; FILETIME FileTime; LARGE_INTEGER TimeData; SYSTEMTIME EndTime; GetSystemTime(&StartTime); SystemTimeToFileTime(&StartTime, &FileTime); TimeData.LowPart = FileTime.dwLowDateTime; TimeData.HighPart = (LONG) FileTime.dwHighDateTime; if ( bIsDC ) { TimeData.QuadPart += YEARCOUNT * 3; } else { TimeData.QuadPart += YEARCOUNT * 100; } FileTime.dwLowDateTime = TimeData.LowPart; FileTime.dwHighDateTime = (DWORD) TimeData.HighPart; FileTimeToSystemTime(&FileTime, &EndTime); *pCertContext = CertCreateSelfSignCertificate( hProv, &SubjectName, 0, &KeyProvInfo, NULL, &StartTime, &EndTime, &certExts ); if (*pCertContext) { HCERTSTORE hStore; hStore = CertOpenSystemStoreW( NULL, L"MY" ); if (hStore) { // // save the temp cert // if(CertAddCertificateContextToStore( hStore, *pCertContext, CERT_STORE_ADD_NEW, NULL) ) { fReturn = TRUE; } else { rc = GetLastError(); } CertCloseStore( hStore, 0 ); } else { rc = GetLastError(); } } else { rc = GetLastError(); } free( certExt.Value.pbData ); } else { rc = GetLastError(); } } else { rc = GetLastError(); } free( SubjectName.pbData ); } else { rc = ERROR_NOT_ENOUGH_MEMORY; } } else { rc = GetLastError(); } free( lpDisplayInfo ); } else { rc = ERROR_NOT_ENOUGH_MEMORY; } CryptDestroyKey( hKey ); } else { rc = GetLastError(); } CryptReleaseContext( hProv, 0 ); } else { rc = GetLastError(); } RpcStringFree( (unsigned short **)&lpContainerName ); } else { rc = ERROR_NOT_ENOUGH_MEMORY; } SetLastError( rc ); if (!fReturn) { if (*pCertContext) { CertFreeCertificateContext( *pCertContext ); *pCertContext = NULL; } } return( fReturn ); } BOOL EncodeAndAlloc( DWORD dwEncodingType, LPCSTR lpszStructType, const void * pvStructInfo, PBYTE * pbEncoded, PDWORD pcbEncoded ) { BOOL b = FALSE; if (CryptEncodeObject( dwEncodingType, lpszStructType, pvStructInfo, NULL, pcbEncoded )) { *pbEncoded = (PBYTE)malloc( *pcbEncoded ); if (*pbEncoded) { if (CryptEncodeObject( dwEncodingType, lpszStructType, pvStructInfo, *pbEncoded, pcbEncoded )) { b = TRUE; } else { free( *pbEncoded ); *pbEncoded = NULL; } } else { SetLastError( ERROR_NOT_ENOUGH_MEMORY ); } } return( b ); } LPWSTR MakeDNName( VOID ) /*++ Routine Description: Fabricates a display name for a locally built self-signed cert Arguments: RecoveryKey - Specifies if this is a recovery key or not. Return Value: Returns a string containing a display name, or NULL. --*/ { LPWSTR DNName = NULL; LPCWSTR DNNameTemplate = L"CN=%ws,L=EFS,OU=EFS File Encryption Certificate"; DWORD cbDNName = 0; DWORD nSize = MAX_COMPUTERNAME_LENGTH + 1; WCHAR lpComputerName[(MAX_COMPUTERNAME_LENGTH + 1) * sizeof( WCHAR )]; BOOL b = GetComputerName ( lpComputerName, &nSize ); if (b) { cbDNName = (wcslen( DNNameTemplate ) + 1) * sizeof( WCHAR ) + (wcslen( lpComputerName ) + 1) * sizeof( WCHAR ); DNName = (LPWSTR)malloc( cbDNName ); if (DNName) { swprintf( DNName, DNNameTemplate, lpComputerName ); } else { SetLastError( ERROR_NOT_ENOUGH_MEMORY ); } } return( DNName ); } 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)malloc( 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 = (ULONG)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 ); } #if 0 BOOLEAN Admin( IN LSA_HANDLE LsaPolicyHandle, IN HANDLE hToken, OUT PSID * Sid ) /*++ Routine Description: This routine determines if the passed token belongs to an administrator. If we are in a domain, the user must be a domain admin for this to succeed. If we are standalne, the user must be in the local administrator's group. Arguments: hToken - Supplies the token of the user to be examined. bDomain - Returns TRUE if the caller is a domain administrator. Sid - Returns the Sid of the user. Return Value: TRUE if the user is an administrator of the type we need, FALSE otherwise. Note that this routine does not attempt to set last error, it either works or it doesn't. --*/ { NET_API_STATUS NetStatus; PSID DomainId; NTSTATUS Status; BOOLEAN fReturn = FALSE; DWORD ReturnLength; PPOLICY_PRIMARY_DOMAIN_INFO PrimaryDomainInfo; // // Initialize OUT parameters // *Sid = NULL; Status = NtQueryInformationToken ( hToken, TokenGroups, NULL, 0, &ReturnLength ); if (STATUS_BUFFER_TOO_SMALL == Status) { PTOKEN_GROUPS Groups = (PTOKEN_GROUPS)malloc( ReturnLength ); if (Groups) { Status = NtQueryInformationToken ( hToken, TokenGroups, Groups, ReturnLength, &ReturnLength ); if (NT_SUCCESS( Status )) { // // We've got the groups, build the SIDs and see if // they're in the token. // // // We're standalone // SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; PSID AdminAccountSid; Status = RtlAllocateAndInitializeSid( &NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdminAccountSid ); if (NT_SUCCESS( Status )) { DWORD i; for (i=0; iGroupCount; i++) { if (RtlEqualSid(Groups->Groups[i].Sid, AdminAccountSid)) { *Sid = AdminAccountSid; fReturn = TRUE; break; } } } } free( Groups ); } if (!fReturn) { // // Something failed, clean up any allocated OUT parameters // if (*Sid) { if (*bDomain == FALSE) { FreeSid( *Sid ); } else { free( *Sid ); } *Sid = NULL; } } } return( fReturn ); } #endif PTOKEN_USER GetTokenUser( HANDLE TokenHandle ) /*++ Routine Description: This routine returns the TOKEN_USER structure for the current user, and optionally, the AuthenticationId from his token. Arguments: AuthenticationId - Supplies an optional pointer to return the AuthenticationId. Return Value: On success, returns a pointer to a TOKEN_USER structure. On failure, returns NULL. Call GetLastError() for more detailed error information. --*/ { NTSTATUS Status; ULONG ReturnLength; TOKEN_STATISTICS TokenStats; PTOKEN_USER pTokenUser = NULL; BOOLEAN b = FALSE; Status = NtQueryInformationToken ( TokenHandle, TokenUser, NULL, 0, &ReturnLength ); if (Status == STATUS_BUFFER_TOO_SMALL) { pTokenUser = (PTOKEN_USER)malloc( ReturnLength ); if (pTokenUser) { Status = NtQueryInformationToken ( TokenHandle, TokenUser, pTokenUser, ReturnLength, &ReturnLength ); if ( NT_SUCCESS( Status )) { // // We're done, mark that everything worked // b = TRUE; } else { SetLastError( RtlNtStatusToDosError( Status )); } if (!b) { // // Something failed, clean up what we were going to return // free( pTokenUser ); pTokenUser = NULL; } } else { SetLastError( RtlNtStatusToDosError( STATUS_INSUFFICIENT_RESOURCES )); } } else { SetLastError( RtlNtStatusToDosError( Status )); } return( pTokenUser ); } VOID InitializeOtherPolicies(LPGROUPPOLICYOBJECT pGPO, HKEY hKeyRoot) { TCHAR szPath[2*MAX_PATH]; GUID guidRIClient = {0x3060e8d0, 0x7020, 0x11d2, 0x84, 0x2d, 0x0, 0xc0, 0x4f, 0xa3, 0x72, 0xd4}; GUID guidRISnap = {0x3060e8ce, 0x7020, 0x11d2, 0x84, 0x2d, 0x0, 0xc0, 0x4f, 0xa3, 0x72, 0xd4}; // // Initialize Remote Install settings // if (SUCCEEDED(pGPO->GetFileSysPath (GPO_SECTION_USER, szPath, 2*MAX_PATH))) { lstrcat (szPath, TEXT("\\Microsoft")); if (!CreateDirectory(szPath, NULL)) { return; } lstrcat (szPath, TEXT("\\RemoteInstall")); if (!CreateDirectory(szPath, NULL)) { return; } lstrcat (szPath, TEXT("\\oscfilter.ini")); WritePrivateProfileString(TEXT("choice"), TEXT("custom"), TEXT("0"), szPath); WritePrivateProfileString(TEXT("choice"), TEXT("tools"), TEXT("0"), szPath); WritePrivateProfileString(TEXT("choice"), TEXT("restart"), TEXT("0"), szPath); pGPO->Save(FALSE, TRUE, &guidRIClient, &guidRISnap); } } HRESULT CreateEFSDefaultPolicy( IN HANDLE hToken, IN PUCHAR *pEfsBlob, IN DWORD *pEfsSize, IN PCCERT_CONTEXT *ppCertContext, IN LPTSTR DomainName ) /*++ Routine Description: Creates the default domain-wide EFS recovery policy object. Arguments: hToken - the current logged on user's token EfsBlob - EFS recovery policy blob EfsSize - the size of the blob ( in bytes) Return: HRESULT --*/ { // // bind to DS to find the domain DNS name // DWORD rc; PLDAP phLdap=NULL; PWSTR DsRootName=NULL; rc = MyLdapOpen(&phLdap); if ( ERROR_SUCCESS == rc ) { rc = MyGetDsObjectRoot( phLdap, &DsRootName ); if ( ERROR_SUCCESS != rc ) { LogEvent(STATUS_SEVERITY_ERROR, GPOEVENT_ERROR_CREATE_GPO, IDS_ERROR_GET_DSROOT, rc ); } } else { LogEvent(STATUS_SEVERITY_ERROR, GPOEVENT_ERROR_CREATE_GPO, IDS_ERROR_BIND_DS, rc ); } HRESULT hr = HRESULT_FROM_WIN32(rc); // if ( SUCCEEDED(hr) && DsRootName ) { /* Get the default recovery policy only when there is no existing one if ( *pEfsBlob == NULL ) { rc = GetDefaultRecoveryPolicy(hToken, TRUE, pEfsBlob, pEfsSize, ppCertContext); if ( ERROR_SUCCESS != rc ) { LogEvent(STATUS_SEVERITY_ERROR, GPOEVENT_ERROR_CREATE_GPO, IDS_ERROR_CREATE_EFSBLOB, rc ); } hr = HRESULT_FROM_WIN32(rc); } */ // } if ( SUCCEEDED(hr) && DsRootName) { LPGROUPPOLICYOBJECT pEfsGPO = NULL; // // OLE is already initialized before this call // create Policy Object instances // TCHAR szPolicyName[MAX_PATH]; pLoadResourceString(IDS_DEFAULT_EFS_POLICY, szPolicyName, MAX_PATH, L"Domain EFS Recovery Policy" ); hr = CreateGroupPolicyObjectInDomain(hToken, DsRootName, szPolicyName, *pEfsBlob, *pEfsSize, *ppCertContext, &pEfsGPO); if ( FAILED(hr) ) { // // if any of the creation failed, delet both objects // if ( pEfsGPO ) { pEfsGPO->Delete(); } } // // release the instances // if ( pEfsGPO ) { pEfsGPO->Release(); } } // // close LDAP port // if ( phLdap ) { MyLdapClose(&phLdap); } if ( DsRootName ) { LocalFree(DsRootName); } return hr; } HRESULT CreateGroupPolicyObjectInDomain( IN HANDLE hToken, IN PWSTR DomainNCName, IN PWSTR GPObjectName, IN PUCHAR EfsBlob, IN ULONG EfsSize, IN PCCERT_CONTEXT pCertContext, OUT LPGROUPPOLICYOBJECT *ppObject ) /*++ Routine Description: Creates a group policy object in DS (and sysvol). Arguments: DomainNCName - The DS domain's ADSI name GPObjectName - the display name for the group policy object to create EfsBlob - The EFS recovery policy blob (ignored if to create default domain policy) EfsSize - the size of EfsBlob (in bytes) ppObject - the created group policy object instance. This will be released by the caller. Return: HRESULT --*/ { LPGROUPPOLICYOBJECT pGPO; HRESULT hr = CoCreateInstance( CLSID_GroupPolicyObject, NULL, CLSCTX_SERVER, IID_IGroupPolicyObject, (PVOID *)&pGPO ); if (SUCCEEDED(hr) && pGPO) { LPWSTR lpPath; // // Build the path to the default GPO // lpPath = (LPWSTR) LocalAlloc (LPTR, (lstrlen(DomainNCName) + 100) * sizeof(WCHAR)); if (lpPath) { lstrcpy (lpPath, TEXT("LDAP://CN={31B2F340-016D-11D2-945F-00C04FB984F9},CN=Policies,CN=System,")); lstrcat (lpPath, (DomainNCName+7)); // // Open the default GPO // hr = pGPO->OpenDSGPO(lpPath, GPO_OPEN_LOAD_REGISTRY); LocalFree (lpPath); if ( SUCCEEDED(hr) ) { // // save EFS blob into the object // HKEY hKeyPolicyRoot; hr = pGPO->GetRegistryKey( GPO_SECTION_MACHINE, &hKeyPolicyRoot ); if (SUCCEEDED(hr)) { // // create EFS recovery policy in the registry // open reg key CERT_EFSBLOB_REGPATH defined in wincrypt.h // DWORD dwDisposition; HKEY hKey; DWORD Win32rc; if ( (Win32rc = RegCreateKeyEx( hKeyPolicyRoot, CERT_EFSBLOB_REGPATH, 0, TEXT("REG_SZ"), REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwDisposition ) ) == ERROR_SUCCESS ) { // // check to see if there is EFS blob // if not, create it. // DWORD RegType; DWORD BlobSize; PUCHAR pNewBlob=NULL; PCCERT_CONTEXT pNewCert=NULL; Win32rc = RegQueryValueEx( hKey, CERT_EFSBLOB_VALUE_NAME, 0, &RegType, NULL, &BlobSize ); if ( Win32rc != ERROR_SUCCESS || BlobSize == 0 ) { if ( EfsBlob == NULL ) { Win32rc = GetDefaultRecoveryPolicy(hToken, TRUE, &pNewBlob, &BlobSize, &pNewCert); if ( ERROR_SUCCESS != Win32rc ) { LogEvent(STATUS_SEVERITY_ERROR, GPOEVENT_ERROR_CREATE_GPO, IDS_ERROR_CREATE_EFSBLOB, Win32rc ); } } else { // // EFS blob is already created, just use it // pNewBlob = EfsBlob; BlobSize = EfsSize; pNewCert = pCertContext; Win32rc = ERROR_SUCCESS; } if ( ERROR_SUCCESS == Win32rc ) { // // add to the cert store of this GPO first // CERT_SYSTEM_STORE_RELOCATE_PARA paraRelocate; paraRelocate.hKeyBase = hKeyPolicyRoot; paraRelocate.pwszSystemStore = L"EFS"; HCERTSTORE hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_W, 0, NULL, CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY | CERT_SYSTEM_STORE_RELOCATE_FLAG, (void *)¶Relocate ); if ( hCertStore ) { if ( !CertAddCertificateContextToStore( hCertStore, pNewCert, // pCertContext, CERT_STORE_ADD_ALWAYS, NULL ) ) { Win32rc = GetLastError(); } // // close the store // CertCloseStore(hCertStore, 0); hCertStore = NULL; } else { Win32rc = GetLastError(); } if ( ERROR_SUCCESS == Win32rc ) { // // set EFS recovery policy binary data to registry // open reg key CERT_EFSBLOB_REGPATH defined in wincrypt.h // Win32rc = RegSetValueEx( hKey, CERT_EFSBLOB_VALUE_NAME, 0, REG_BINARY, (PBYTE)pNewBlob, // EfsBlob BlobSize // EfsSize ); if (Win32rc == ERROR_SUCCESS) { hr = pGPO->Save(TRUE, TRUE, &guidExtension, &guidSnapin); hr = pGPO->Save(TRUE, TRUE, &guidRegExt, &guidSnapin ); if ( FAILED(hr) ) { LogEvent(STATUS_SEVERITY_ERROR, GPOEVENT_ERROR_CREATE_GPO, IDS_ERROR_SAVE_GPO, hr, GPObjectName ); } } else { LogEvent(STATUS_SEVERITY_ERROR, GPOEVENT_ERROR_CREATE_GPO, IDS_ERROR_SAVE_EFSBLOB, Win32rc ); } } else { LogEvent(STATUS_SEVERITY_ERROR, GPOEVENT_ERROR_CREATE_GPO, IDS_ERROR_ADD_CERTIFICATE, Win32rc ); } // // free allocated blob and certificate // if ( pNewBlob != EfsBlob ) { if ( pNewBlob ) { free(pNewBlob); } if ( pNewCert ) { CertFreeCertificateContext( pNewCert ); } } } else { LogEvent(STATUS_SEVERITY_ERROR, GPOEVENT_ERROR_CREATE_GPO, IDS_ERROR_CREATE_EFSBLOB, Win32rc ); } } // // close the registry key // RegCloseKey(hKey); } else { LogEvent(STATUS_SEVERITY_ERROR, GPOEVENT_ERROR_CREATE_GPO, IDS_ERROR_OPEN_EFSKEY, Win32rc ); } if ( Win32rc != ERROR_SUCCESS ) { hr = HRESULT_FROM_WIN32(Win32rc); } InitializeOtherPolicies(pGPO, hKeyPolicyRoot); RegCloseKey(hKeyPolicyRoot); } else { LogEvent(STATUS_SEVERITY_ERROR, GPOEVENT_ERROR_CREATE_GPO, IDS_ERROR_GETGPO_REGKEY, hr, GPObjectName ); } } else { LogEvent(STATUS_SEVERITY_ERROR, GPOEVENT_ERROR_CREATE_GPO, IDS_ERROR_NEW_GPO, hr, GPObjectName, DomainNCName ); } } } else { LogEvent(STATUS_SEVERITY_ERROR, GPOEVENT_ERROR_CREATE_GPO, IDS_ERROR_CREATE_GPO_INSTANCE, hr ); } if ( SUCCEEDED(hr) ) { *ppObject = pGPO; } else if ( pGPO ) { // // failed, release the instance // pGPO->Release(); } return hr; } DWORD MyGetDsObjectRoot( IN PLDAP pLdap, OUT PWSTR *pDsRootName ) /*++ Routine Description: Get the root domain name of the current domain. The returned domain's name is in ADSI format, for example, LDAP://DC=test_dom,DC=ntdev,DC=microsoft,DC=com Arguments: pLdap - the ldap handle pDsRootName - the domain's ADSI name to output Return: Win32 error --*/ { DWORD retErr; LDAPMessage *Message = NULL; // for LDAP calls. PWSTR Attribs[2]; // for LDAP calls. if ( pLdap == NULL || pDsRootName == NULL ) { return(ERROR_INVALID_PARAMETER); } Attribs[0] = L"defaultNamingContext"; Attribs[1] = NULL; retErr = ldap_search_s(pLdap, L"", LDAP_SCOPE_BASE, L"(objectClass=*)", Attribs, 0, &Message); if( Message ) { // should not check for error code retErr = ERROR_SUCCESS; LDAPMessage *Entry = NULL; // // read the first entry. // we did base level search, we have only one entry. // Entry does not need to be freed (it is freed with the message) // Entry = ldap_first_entry(pLdap, Message); if(Entry != NULL) { PWSTR *Values = ldap_get_values(pLdap, Entry, Attribs[0]); if(Values != NULL) { // // should only get one value for the default naming context // Values[0] here is the DN. // *pDsRootName = (PWSTR)LocalAlloc(0, (wcslen(Values[0])+1+7)*sizeof(WCHAR)); if ( *pDsRootName ) { swprintf(*pDsRootName, L"LDAP://%s\0", Values[0]); } else { retErr = ERROR_NOT_ENOUGH_MEMORY; } ldap_value_free(Values); } else retErr = LdapMapErrorToWin32(pLdap->ld_errno); } else retErr = LdapMapErrorToWin32(pLdap->ld_errno); ldap_msgfree(Message); Message = NULL; } return(retErr); } DWORD MyLdapOpen( OUT PLDAP *pLdap ) /*++ Routine Description: Open a LDAP port and bind to it. Arguments: pLdap - the ldap handle to output Return: Win32 error --*/ { DWORD Win32rc; WCHAR sysName[256]; DWORD dSize = (sizeof(sysName) / sizeof(sysName[0])); if ( pLdap == NULL ) { return(ERROR_INVALID_PARAMETER); } // // get current computer and IP address // if ( !GetComputerName(sysName, &dSize) ) { Win32rc = GetLastError(); } else { PDOMAIN_CONTROLLER_INFOW DCInfo; // for DsGetDcName HINSTANCE hDsGetDcDll = LoadLibrary(TEXT("netapi32.dll")); PFNDSGETDCNAME pfnDsGetDcName=NULL; if ( hDsGetDcDll) { #if defined(UNICODE) pfnDsGetDcName = (PFNDSGETDCNAME)GetProcAddress(hDsGetDcDll, "DsGetDcNameW"); #else pfnDsGetDcName = (PFNDSGETDCNAME)GetProcAddress(hDsGetDcDll, "DsGetDcNameA"); #endif } if ( pfnDsGetDcName ) { Win32rc = (*pfnDsGetDcName)(sysName, NULL, NULL, NULL, DS_IP_REQUIRED, &DCInfo ); } else { Win32rc = ERROR_PROC_NOT_FOUND; } if ( hDsGetDcDll ) { FreeLibrary(hDsGetDcDll); } if(Win32rc == ERROR_SUCCESS) { PWSTR pwszAddress = DCInfo[0].DomainControllerAddress; if(*pwszAddress == L'\\') { pwszAddress += 2; } // // bind to ldap // *pLdap = ldap_open(pwszAddress, LDAP_PORT); if ( *pLdap == NULL ) { Win32rc = ERROR_FILE_NOT_FOUND; } else { Win32rc = ldap_bind_s(*pLdap, NULL, NULL, LDAP_AUTH_SSPI); } // // free DCInfo // LocalFree(DCInfo); } } return(Win32rc); } DWORD MyLdapClose( IN PLDAP *pLdap ) /*++ Routine Description: Close the LDAP bind. Arguments: pLdap - the ldap handle Return: Win32 error --*/ { if ( pLdap != NULL ) { // // unbind pLDAP // if ( *pLdap != NULL ) ldap_unbind(*pLdap); *pLdap = NULL; } return(ERROR_SUCCESS); } //************************************************************* // Routines to handle events //************************************************************* BOOL InitializeEvents (void) /*++ Routine Description: Opens the event log Arguments: None Return: TRUE if successful FALSE if an error occurs --*/ { // // Open the event source // hEventLog = RegisterEventSource(NULL, EventSourceName); if (hEventLog) { return TRUE; } return FALSE; } int LogEvent( IN DWORD LogLevel, IN DWORD dwEventID, IN UINT idMsg, ...) /*++ Routine Description: Logs a verbose event to the event log Arguments: bLogLevel - the severity level of the log STATUS_SEVERITY_SUCCESS STATUS_SEVERITY_INFORMATIONAL STATUS_SEVERITY_WARNING STATUS_SEVERITY_ERROR dwEventID - the event ID (defined in uevents.mc) idMsg - Message id Return: TRUE if successful FALSE if an error occurs --*/ { TCHAR szMsg[MAX_PATH]; TCHAR szErrorMsg[2*MAX_PATH+40]; LPTSTR aStrings[2]; WORD wType; va_list marker; // // Load the message // if (idMsg != 0) { pLoadResourceString(idMsg, szMsg, MAX_PATH, TEXT("Error loading resource string. Params : %x")); } else { lstrcpy (szMsg, TEXT("%s")); } // // Plug in the arguments // szErrorMsg[0] = L'\0'; va_start(marker, idMsg); __try { wvsprintf(szErrorMsg, szMsg, marker); } __except(EXCEPTION_EXECUTE_HANDLER) { } va_end(marker); // // Check for the event log being open. // if (!hEventLog) { if (!InitializeEvents()) { return -1; } } // // Report the event to the eventlog // aStrings[0] = szErrorMsg; switch (LogLevel) { case STATUS_SEVERITY_WARNING: wType = EVENTLOG_WARNING_TYPE; break; case STATUS_SEVERITY_SUCCESS: wType = EVENTLOG_SUCCESS; break; case STATUS_SEVERITY_ERROR: wType = EVENTLOG_ERROR_TYPE; break; default: wType = EVENTLOG_INFORMATION_TYPE; break; } if (!ReportEvent(hEventLog, wType, 0, dwEventID, NULL, 1, 0, (LPCTSTR *)aStrings, NULL) ) { return 1; } return 0; } BOOL ShutdownEvents (void) /*++ Routine Description: Stops the event log Arguments: None Return: TRUE if successful FALSE if an error occurs --*/ { BOOL bRetVal = TRUE; if (hEventLog) { bRetVal = DeregisterEventSource(hEventLog); hEventLog = NULL; } return bRetVal; } LPWSTR GetCertDisplayInformation( IN PCCERT_CONTEXT pCertContext ) /*++ Routine Description: Returns the display string from the passed certificate context. Arguments: pCertContext - Supplies a pointer to an open certificate context. Return Value: On success, pointer to display string. Caller must call free() to free. NULL on failure. --*/ { DWORD Format = CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG; // // First, try to get the email name // DWORD cchNameString; LPWSTR wszNameString = NULL; cchNameString = CertGetNameString( pCertContext, CERT_NAME_RDN_TYPE, 0, &Format, NULL, 0 ); if (cchNameString != 1) { // // String was found // wszNameString = (LPWSTR)malloc( cchNameString * sizeof( WCHAR )); if (wszNameString) { cchNameString = CertGetNameString( pCertContext, CERT_NAME_RDN_TYPE, 0, &Format, wszNameString, cchNameString ); } else { SetLastError( ERROR_NOT_ENOUGH_MEMORY ); } } else { // // This shouldn't happen. If it does, we'll return NULL. // Try to complain about it. // ASSERT( FALSE ); } return( wszNameString ); } BOOLEAN IsSelfSignedPolicyExpired( IN PRECOVERY_POLICY_1_1 RecoveryPolicy OPTIONAL ) /*++ Routine Description: This routine checks if the existing recovery cert expiring within one year Arguments: PolicyEfsInfo - Supplies a pointer to the current EFS recovery policy. Return Value: return-value - TRUE if it is within one year --*/ { BOOLEAN GetNewCert = TRUE; if (RecoveryPolicy == NULL) { // // We need to create a new one if none exists. // return TRUE; } if (RecoveryPolicy->RecoveryPolicyHeader.RecoveryKeyCount > 1){ // // Default recovery policy changed. // return FALSE; } __try { // // Scan the recovery data looking for recovery keys in a format we understand // PEFS_PUBLIC_KEY_INFO PublicKeyInfo = &((PRECOVERY_KEY_1_1) &(RecoveryPolicy->RecoveryKeyList[0]))->PublicKeyInfo; if (PublicKeyInfo->KeySourceTag != EfsCertificate) { // // Out dated recovery cert. Get a new one // return TRUE; } PBYTE pbCert = (PBYTE)OFFSET_TO_POINTER(CertificateInfo.Certificate, PublicKeyInfo); DWORD cbCert = PublicKeyInfo->CertificateInfo.CertificateLength; PCCERT_CONTEXT pCertContext = CertCreateCertificateContext( CRYPT_ASN_ENCODING, (const PBYTE)pbCert, cbCert); if (pCertContext) { SYSTEMTIME CertTime; SYSTEMTIME CrntTime; if (FileTimeToSystemTime(&(pCertContext->pCertInfo->NotAfter), &CertTime)){ GetSystemTime( &CrntTime); if ( CertTime.wYear <= CrntTime.wYear + 1) { // // Get the display information // LPWSTR lpDisplayInfo = GetCertDisplayInformation( pCertContext ); if (lpDisplayInfo ) { if (!wcstok( lpDisplayInfo, L"OU=EFS File Encryption Certificate" )){ GetNewCert = FALSE; } free(lpDisplayInfo); } } else { GetNewCert = FALSE; } } CertFreeCertificateContext( pCertContext ); } } __except (EXCEPTION_EXECUTE_HANDLER) { // // There was something wrong with the recovery policy. // Get a new recovery cert. // } return GetNewCert; }