/****************************************************************************** * * Copyright (c) 2000 Microsoft Corporation * * Module Name: * srpasswd.cpp * * Abstract: * password filter routines to restore user's latest passwords * * Revision History: * Henry Lee (henrylee) 06/27/2000 created * *****************************************************************************/ #include "stdwin.h" #include #include extern "C" { #include #include } #include "rstrcore.h" extern CSRClientLoader g_CSRClientLoader; //+--------------------------------------------------------------------------- // // Function: RegisterNotificationDLL // // Synopsis: registers/unregisters this DLL // // Arguments: [fRegister] -- TRUE to register, FALSE to unregister // [hKeyLM] -- key for HKEY_LOCAL_MACHINE or System // // History: 12-Apr-2000 HenryLee Created // //---------------------------------------------------------------------------- DWORD RegisterNotificationDLL (HKEY hKeyLM, BOOL fRegister) { HKEY hKey = NULL; DWORD dwErr = ERROR_SUCCESS; ULONG ulType; ULONG ulSize = MAX_PATH * sizeof(WCHAR); WCHAR wcsBuffer[MAX_PATH]; WCHAR wcsFileName[MAX_PATH]; GetModuleFileNameW (g_hInst, wcsFileName, MAX_PATH); const ULONG ccFileName = lstrlenW (wcsFileName) + 1; if (hKeyLM == HKEY_LOCAL_MACHINE) { lstrcpy (wcsBuffer, L"System\\CurrentControlSet\\Control\\Lsa"); } else { lstrcpy(wcsBuffer, L"CurrentControlSet\\Control\\Lsa"); ChangeCCS(hKeyLM, wcsBuffer); } dwErr = RegOpenKeyExW (hKeyLM, wcsBuffer, 0, KEY_READ | KEY_WRITE, &hKey); if (dwErr != ERROR_SUCCESS) goto Err; dwErr = RegQueryValueEx (hKey, L"Notification Packages", 0, &ulType, (BYTE *) wcsBuffer, &ulSize); if (dwErr != ERROR_SUCCESS) goto Err; for (ULONG i=0; i < ulSize/sizeof(WCHAR); i += lstrlenW(&wcsBuffer[i])+1) { if (fRegister) // append at end { if (lstrcmpi (&wcsBuffer[i], wcsFileName) == 0) goto Err; // it's already registered if (wcsBuffer[i] == L'\0') // end of list { lstrcpy (&wcsBuffer[i], wcsFileName); wcsBuffer[ i + ccFileName ] = L'\0'; // add double NULL ulSize += ccFileName * sizeof(WCHAR); break; } } else // remove from the end { if (lstrcmpi (&wcsBuffer[i], wcsFileName) == 0) { wcsBuffer[i] = L'\0'; ulSize -= ccFileName * sizeof(WCHAR); break; } } } dwErr = RegSetValueExW (hKey, L"Notification Packages", 0, ulType, (BYTE *) wcsBuffer, ulSize); Err: if (hKey != NULL) RegCloseKey (hKey); return dwErr; } //+--------------------------------------------------------------------------- // // Function: GetDomainId // // Synopsis: stolen from setup, get the local domain ID // // Arguments: [ServerHandle] -- handle to the local SAM server // [pDomainId] -- output domain ID // // History: 12-Apr-2000 HenryLee Created // //---------------------------------------------------------------------------- NTSTATUS GetDomainId (SAM_HANDLE ServerHandle, PSID * pDomainId ) { NTSTATUS status = STATUS_SUCCESS; SAM_ENUMERATE_HANDLE EnumContext; PSAM_RID_ENUMERATION EnumBuffer = NULL; DWORD CountReturned = 0; PSID LocalDomainId = NULL; DWORD LocalBuiltinDomainSid[sizeof(SID) / sizeof(DWORD) + SID_MAX_SUB_AUTHORITIES]; SID_IDENTIFIER_AUTHORITY BuiltinAuthority = SECURITY_NT_AUTHORITY; BOOL bExit = FALSE; // // Compute the builtin domain sid. // RtlInitializeSid((PSID) LocalBuiltinDomainSid, &BuiltinAuthority, 1); *(RtlSubAuthoritySid((PSID)LocalBuiltinDomainSid, 0)) = SECURITY_BUILTIN_DOMAIN_RID; // // Loop getting the list of domain ids from SAM // EnumContext = 0; do { // // Get several domain names. // status = SamEnumerateDomainsInSamServer ( ServerHandle, &EnumContext, (PVOID *) &EnumBuffer, 8192, &CountReturned ); if (!NT_SUCCESS (status)) { goto exit; } if (status != STATUS_MORE_ENTRIES) { bExit = TRUE; } // // Lookup the domain ids for the domains // for (ULONG i = 0; i < CountReturned; i++) { // // Free the sid from the previous iteration. // if (LocalDomainId != NULL) { SamFreeMemory (LocalDomainId); LocalDomainId = NULL; } // // Lookup the domain id // status = SamLookupDomainInSamServer ( ServerHandle, &EnumBuffer[i].Name, &LocalDomainId ); if (!NT_SUCCESS (status)) { goto exit; } if (RtlEqualSid ((PSID)LocalBuiltinDomainSid, LocalDomainId)) { continue; } *pDomainId = LocalDomainId; LocalDomainId = NULL; status = STATUS_SUCCESS; goto exit; } SamFreeMemory(EnumBuffer); EnumBuffer = NULL; } while (!bExit); status = STATUS_NO_SUCH_DOMAIN; exit: if (EnumBuffer != NULL) SamFreeMemory(EnumBuffer); return status; } //+--------------------------------------------------------------------------- // // Function: ForAllUsers // // Synopsis: iterate password changing for all local users // // Arguments: [hSam] -- handle to open SAM hive // [hSecurity] -- handle to open SECURITY hive // [hSystem] -- handle to open SYSTEM hive // // History: 12-Apr-2000 HenryLee Created // //---------------------------------------------------------------------------- NTSTATUS ForAllUsers (HKEY hSam, HKEY hSecurity, HKEY hSystem) { NTSTATUS nts = STATUS_SUCCESS; NTSTATUS ntsEnum = STATUS_SUCCESS; BOOLEAN bPresent; BOOLEAN bNonNull; SAM_HANDLE ServerHandle = NULL; SAM_HANDLE DomainHandle = NULL; SAM_HANDLE UserHandle; SAM_ENUMERATE_HANDLE EnumerationContext = NULL; SAM_RID_ENUMERATION *SamRidEnumeration; ULONG CountOfEntries; ULONG UserRid; UNICODE_STRING us; PSID LocalDomainId = NULL; USER_INTERNAL1_INFORMATION UserPasswordInfo; RtlInitUnicodeString (&us, L""); // this machine nts = SamConnect (&us, &ServerHandle, SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN | SAM_SERVER_ENUMERATE_DOMAINS, NULL); if (!NT_SUCCESS(nts)) goto Err; nts = GetDomainId (ServerHandle, &LocalDomainId); if (!NT_SUCCESS(nts)) goto Err; nts = SamOpenDomain( ServerHandle, DOMAIN_READ | DOMAIN_LIST_ACCOUNTS | DOMAIN_LOOKUP | DOMAIN_READ_PASSWORD_PARAMETERS, LocalDomainId, &DomainHandle ); if (!NT_SUCCESS(nts)) goto Err; do { ntsEnum = nts = SamEnumerateUsersInDomain ( DomainHandle, &EnumerationContext, 0, (PVOID *) &SamRidEnumeration, 0, &CountOfEntries); if (nts != STATUS_MORE_ENTRIES && !NT_SUCCESS(nts)) { goto Err; } for (UINT i=0; i < CountOfEntries; i++) { ULONG UserRid = SamRidEnumeration[i].RelativeId; nts = SamRetrieveOwfPasswordUser( UserRid, hSecurity, hSam, hSystem, NULL, /* boot key not supported */ 0, /* boot key not supported */ &UserPasswordInfo.NtOwfPassword, &bPresent, &bNonNull); if (!NT_SUCCESS(nts)) continue; nts = SamOpenUser (DomainHandle, USER_READ_ACCOUNT | USER_WRITE_ACCOUNT | USER_CHANGE_PASSWORD | USER_FORCE_PASSWORD_CHANGE, UserRid, &UserHandle); if (NT_SUCCESS(nts)) { UserPasswordInfo.NtPasswordPresent = bPresent; UserPasswordInfo.LmPasswordPresent = FALSE; UserPasswordInfo.PasswordExpired = FALSE; nts = SamSetInformationUser(UserHandle, UserInternal1Information, &UserPasswordInfo); SamCloseHandle (UserHandle); } } SamFreeMemory (SamRidEnumeration); } while (ntsEnum == STATUS_MORE_ENTRIES); Err: if (ServerHandle != NULL) SamCloseHandle (ServerHandle); if (DomainHandle != NULL) SamCloseHandle (DomainHandle); if (LocalDomainId != NULL) SamFreeMemory (LocalDomainId); return nts; } //+--------------------------------------------------------------------------- // // Function: RestoreLsaSecrets // // Synopsis: restore machine account and autologon passwords // // Arguments: // // History: 12-Apr-2000 HenryLee Created // //---------------------------------------------------------------------------- DWORD RestoreLsaSecrets () { HKEY hKey = NULL; LSA_OBJECT_ATTRIBUTES loa; LSA_HANDLE hLsa = NULL; DWORD dwErr = ERROR_SUCCESS; ULONG ulSize = 0; ULONG ulType = 0; WCHAR wcsBuffer [MAX_PATH]; loa.Length = sizeof(LSA_OBJECT_ATTRIBUTES); loa.RootDirectory = NULL; loa.ObjectName = NULL; loa.Attributes = 0; loa.SecurityDescriptor = NULL; loa.SecurityQualityOfService = NULL; if (LSA_SUCCESS (LsaOpenPolicy(NULL, &loa, POLICY_VIEW_LOCAL_INFORMATION, &hLsa))) { dwErr = RegOpenKeyExW (HKEY_LOCAL_MACHINE, s_cszSRRegKey, 0, KEY_READ | KEY_WRITE, &hKey); if (dwErr != ERROR_SUCCESS) goto Err; ulSize = MAX_PATH * sizeof(WCHAR); if (ERROR_SUCCESS == RegQueryValueEx (hKey, s_cszMachineSecret, 0, &ulType, (BYTE *) wcsBuffer, &ulSize)) { wcsBuffer [ulSize / 2] = L'\0'; dwErr = SetLsaSecret (hLsa, s_cszMachineSecret, wcsBuffer); if (ERROR_SUCCESS != dwErr) goto Err; RegDeleteValueW (hKey, s_cszMachineSecret); } ulSize = MAX_PATH * sizeof(WCHAR); if (ERROR_SUCCESS == RegQueryValueEx (hKey, s_cszAutologonSecret, 0, &ulType, (BYTE *) wcsBuffer, &ulSize)) { wcsBuffer [ulSize / 2] = L'\0'; dwErr = SetLsaSecret (hLsa, s_cszAutologonSecret, wcsBuffer); if (ERROR_SUCCESS != dwErr) goto Err; RegDeleteValueW (hKey, s_cszAutologonSecret); } } Err: if (hKey != NULL) RegCloseKey (hKey); return dwErr; } //+--------------------------------------------------------------------------- // // Function: RestoreRIDs // // Synopsis: restore next availble RID and password // // Arguments: // // History: 12-Apr-2000 HenryLee Created // //---------------------------------------------------------------------------- DWORD RestoreRIDs (WCHAR *pszSamPath) { HKEY hKeySam = NULL; DWORD dwErr = ERROR_SUCCESS; ULONG ulNextRid = 0; ULONG ulOldRid = 0; TENTER("RestoreRIDs"); dwErr = RegLoadKeyW (HKEY_LOCAL_MACHINE, s_cszRestoreSAMHiveName, pszSamPath); if (dwErr != ERROR_SUCCESS) { trace(0, "! RegLoadKeyW : %ld", dwErr); goto Err; } dwErr = RegOpenKeyW (HKEY_LOCAL_MACHINE, s_cszSamHiveName, &hKeySam); if (dwErr != ERROR_SUCCESS) { trace(0, "! RegOpenKeyW on %S: %ld", s_cszSamHiveName, dwErr); goto Err; } dwErr = RtlNtStatusToDosError(SamGetNextAvailableRid (hKeySam, &ulNextRid)); if (dwErr != ERROR_SUCCESS) { trace(0, "! SamGetNextAvailableRid : %ld", dwErr); goto Err; } RegCloseKey (hKeySam); hKeySam = NULL; dwErr = RegOpenKeyW (HKEY_LOCAL_MACHINE, s_cszRestoreSAMHiveName, &hKeySam); if (dwErr != ERROR_SUCCESS) { trace(0, "! RegOpenKeyW on %S: %ld", s_cszRestoreSAMHiveName, dwErr); goto Err; } // as an optimization we don't set the RID if it didn't change if (NT_SUCCESS(SamGetNextAvailableRid (hKeySam, &ulOldRid)) && ulNextRid > ulOldRid) { dwErr = RtlNtStatusToDosError(SamSetNextAvailableRid (hKeySam, ulNextRid)); if (dwErr != ERROR_SUCCESS) { trace(0, "! SamSetNextAvailableRid : %ld", dwErr); } } Err: if (hKeySam != NULL) { RegCloseKey (hKeySam); } RegUnLoadKeyW (HKEY_LOCAL_MACHINE, s_cszRestoreSAMHiveName); TLEAVE(); return dwErr; } DWORD RestorePasswords () { HKEY hKeySam = NULL, hKeySecurity = NULL, hKeySystem = NULL; DWORD dwErr = ERROR_SUCCESS; WCHAR wcsSystem [MAX_PATH]; WCHAR wcsPath [MAX_PATH]; BOOLEAN OldPriv; CRestorePoint rp; DWORD dwTemp; InitAsyncTrace(); TENTER("RestorePasswords"); // Attempt to get restore privilege dwErr = RtlNtStatusToDosError (RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &OldPriv)); if (dwErr != ERROR_SUCCESS) { trace(0, "! RtlAdjustPrivilege : %ld", dwErr); goto Err0; } if (FALSE == GetSystemDrive (wcsSystem)) { dwErr = GetLastError(); trace(0, "! GetSystemDrive : %ld", dwErr); goto Err; } dwErr = GetCurrentRestorePoint(rp); if (dwErr != ERROR_SUCCESS) { trace(0, "! GetCurrentRestorePoint : %ld", dwErr); goto Err; } MakeRestorePath (wcsPath, wcsSystem, rp.GetDir()); lstrcatW (wcsPath, SNAPSHOT_DIR_NAME); lstrcatW (wcsPath, L"\\"); lstrcatW (wcsPath, s_cszHKLMFilePrefix); lstrcatW (wcsPath, s_cszSamHiveName); dwErr = RegLoadKeyW (HKEY_LOCAL_MACHINE, s_cszRestoreSAMHiveName, wcsPath); if (dwErr != ERROR_SUCCESS) { trace(0, "! RegLoadKeyW on %S: %ld", s_cszRestoreSAMHiveName, dwErr); goto Err; } dwErr = RegOpenKeyW (HKEY_LOCAL_MACHINE, s_cszRestoreSAMHiveName, &hKeySam); if (dwErr != ERROR_SUCCESS) { trace(0, "! RegOpenKeyW on %S: %ld", s_cszRestoreSAMHiveName, dwErr); goto Err; } MakeRestorePath (wcsPath, wcsSystem, rp.GetDir()); lstrcatW (wcsPath, SNAPSHOT_DIR_NAME); lstrcatW (wcsPath, L"\\"); lstrcatW (wcsPath, s_cszHKLMFilePrefix); lstrcatW (wcsPath, s_cszSecurityHiveName); dwErr = RegLoadKeyW (HKEY_LOCAL_MACHINE, s_cszRestoreSECURITYHiveName, wcsPath); if (dwErr != ERROR_SUCCESS) { trace(0, "! RegLoadKeyW on %S: %ld", s_cszRestoreSECURITYHiveName, dwErr); goto Err; } dwErr = RegOpenKeyW (HKEY_LOCAL_MACHINE, s_cszRestoreSECURITYHiveName,&hKeySecurity); if (dwErr != ERROR_SUCCESS) { trace(0, "! RegOpenKeyW on %S: %ld", s_cszRestoreSECURITYHiveName, dwErr); goto Err; } MakeRestorePath (wcsPath, wcsSystem, rp.GetDir()); lstrcatW (wcsPath, SNAPSHOT_DIR_NAME); lstrcatW (wcsPath, L"\\"); lstrcatW (wcsPath, s_cszHKLMFilePrefix); lstrcatW (wcsPath, s_cszSystemHiveName); dwErr = RegLoadKeyW (HKEY_LOCAL_MACHINE, s_cszRestoreSYSTEMHiveName, wcsPath); if (dwErr != ERROR_SUCCESS) { trace(0, "! RegLoadKeyW on %S: %ld", s_cszRestoreSYSTEMHiveName, dwErr); goto Err; } dwErr = RegOpenKeyW (HKEY_LOCAL_MACHINE, s_cszRestoreSYSTEMHiveName, &hKeySystem); if (dwErr != ERROR_SUCCESS) { trace(0, "! RegOpenKeyW on %S: %ld", s_cszRestoreSYSTEMHiveName, dwErr); goto Err; } dwErr = RtlNtStatusToDosError(ForAllUsers(hKeySam,hKeySecurity,hKeySystem)); if (dwErr != ERROR_SUCCESS) { trace(0, "! ForAllUsers : %ld", dwErr); goto Err; } dwErr = RestoreLsaSecrets (); if (dwErr != ERROR_SUCCESS) { trace(0, "! RestoreLsaSecrets : %ld", dwErr); } Err: if (hKeySam != NULL) { RegCloseKey (hKeySam); } dwTemp = RegUnLoadKeyW (HKEY_LOCAL_MACHINE, s_cszRestoreSAMHiveName); if (ERROR_SUCCESS != dwTemp) { trace(0, "! RegUnLoadKeyW 0n %S : %ld", s_cszRestoreSAMHiveName, dwTemp); } if (hKeySecurity != NULL) { RegCloseKey (hKeySecurity); } dwTemp = RegUnLoadKeyW (HKEY_LOCAL_MACHINE, s_cszRestoreSECURITYHiveName); if (ERROR_SUCCESS != dwTemp) { trace(0, "! RegUnLoadKeyW 0n %S : %ld", s_cszRestoreSECURITYHiveName, dwTemp); } if (hKeySystem != NULL) { RegCloseKey (hKeySystem); } dwTemp = RegUnLoadKeyW (HKEY_LOCAL_MACHINE, s_cszRestoreSYSTEMHiveName); if (ERROR_SUCCESS != dwTemp) { trace(0, "! RegUnLoadKeyW 0n %S : %ld", s_cszRestoreSYSTEMHiveName, dwTemp); } // restore the old privilege RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, OldPriv, FALSE, &OldPriv); Err0: // unregister this notification package RegisterNotificationDLL (HKEY_LOCAL_MACHINE, FALSE); TermAsyncTrace(); return dwErr; } //+--------------------------------------------------------------------------- // // Function: WaitForSAM // // Synopsis: waits for SAM database to initialize // // Arguments: // // History: 12-Apr-2000 HenryLee Created // //---------------------------------------------------------------------------- DWORD WINAPI WaitForSAM (VOID *pv) { NTSTATUS nts = STATUS_SUCCESS; DWORD dwErr = ERROR_SUCCESS; DWORD WaitStatus; UNICODE_STRING EventName; HANDLE EventHandle; OBJECT_ATTRIBUTES EventAttributes; // Load SRClient g_CSRClientLoader.LoadSrClient(); // // open SAM event // RtlInitUnicodeString( &EventName, L"\\SAM_SERVICE_STARTED"); InitializeObjectAttributes( &EventAttributes, &EventName, 0, 0, NULL ); nts = NtOpenEvent( &EventHandle, SYNCHRONIZE|EVENT_MODIFY_STATE, &EventAttributes ); if ( !NT_SUCCESS(nts)) { if( nts == STATUS_OBJECT_NAME_NOT_FOUND ) { // // SAM hasn't created this event yet, let us create it now. // SAM opens this event to set it. // nts = NtCreateEvent( &EventHandle, SYNCHRONIZE|EVENT_MODIFY_STATE, &EventAttributes, NotificationEvent, FALSE ); // The event is initially not signaled if( nts == STATUS_OBJECT_NAME_EXISTS || nts == STATUS_OBJECT_NAME_COLLISION ) { // // second chance, if the SAM created the event before we did // nts = NtOpenEvent( &EventHandle, SYNCHRONIZE|EVENT_MODIFY_STATE, &EventAttributes ); } } } // // Loop waiting. // if (NT_SUCCESS(nts)) { WaitStatus = WaitForSingleObject( EventHandle, 60*1000 ); // 60 Seconds if ( WaitStatus == WAIT_TIMEOUT ) { nts = STATUS_TIMEOUT; } else if ( WaitStatus != WAIT_OBJECT_0 ) { nts = STATUS_UNSUCCESSFUL; } } (VOID) NtClose( EventHandle ); if (NT_SUCCESS(nts)) // Okay, SAM is available { dwErr = RestorePasswords(); } else { dwErr = RtlNtStatusToDosError (nts); } return dwErr; } //+--------------------------------------------------------------------------- // // Function: InitializeChangeNotify and PasswordChangeNotify // // Synopsis: callback functions from SAM // // Arguments: // // History: 12-Apr-2000 HenryLee Created // //---------------------------------------------------------------------------- BOOLEAN NTAPI InitializeChangeNotify () { // we will call LoadSRClient from WaitForSAM HANDLE hThread = CreateThread (NULL, 0, WaitForSAM, NULL, 0, NULL); return TRUE; } NTSTATUS NTAPI PasswordChangeNotify ( PUNICODE_STRING UserName, ULONG RelativeId, PUNICODE_STRING NewPassword ) { return STATUS_SUCCESS; }