/*++ Copyright (c) 1996 Microsoft Corporation Module Name: analyze.c Abstract: Routines to analyze a system. The analysis information is saved in memory Author: Jin Huang (jinhuang) 25-Nov-1996 Revision History: --*/ #include "headers.h" #include "serverp.h" #include "pfp.h" #include "infp.h" #include "service.h" #include "regvalue.h" #include "authz.h" #if _WIN32_WINNT>=0x0500 #include "kerberos.h" #endif #include #include #pragma hdrstop #define Add2Ptr(pv, cb) ((BYTE *) pv + cb) #define PSD_BASE_LENGTH 100 // // properties of SAP engine (thread safe variables) // #define SE_VALID_CONTROL_BITS ( SE_DACL_UNTRUSTED | \ SE_SERVER_SECURITY | \ SE_DACL_AUTO_INHERIT_REQ | \ SE_SACL_AUTO_INHERIT_REQ | \ SE_DACL_AUTO_INHERITED | \ SE_SACL_AUTO_INHERITED | \ SE_DACL_PROTECTED | \ SE_SACL_PROTECTED ) extern PSCECONTEXT Thread hProfile; PSCESECTION Thread hSection=NULL; extern AUTHZ_RESOURCE_MANAGER_HANDLE ghAuthzResourceManager; DWORD Thread BadCnt; DWORD Thread gOptions=0; static PWSTR AccessItems[] = { {(PWSTR)TEXT("MinimumPasswordAge")}, {(PWSTR)TEXT("MaximumPasswordAge")}, {(PWSTR)TEXT("MinimumPasswordLength")}, {(PWSTR)TEXT("PasswordComplexity")}, {(PWSTR)TEXT("PasswordHistorySize")}, {(PWSTR)TEXT("LockoutBadCount")}, {(PWSTR)TEXT("ResetLockoutCount")}, {(PWSTR)TEXT("LockoutDuration")}, {(PWSTR)TEXT("RequireLogonToChangePassword")}, {(PWSTR)TEXT("ForceLogoffWhenHourExpire")}, {(PWSTR)TEXT("ClearTextPassword")}, {(PWSTR)TEXT("LSAAnonymousNameLookup")}, {(PWSTR)TEXT("EnableAdminAccount")}, {(PWSTR)TEXT("EnableGuestAccount")} }; #define MAX_ACCESS_ITEMS sizeof(AccessItems)/sizeof(PWSTR) #define IDX_MIN_PASS_AGE 0 #define IDX_MAX_PASS_AGE 1 #define IDX_MIN_PASS_LEN 2 #define IDX_PASS_COMPLEX 3 #define IDX_PASS_HISTORY 4 #define IDX_LOCK_COUNT 5 #define IDX_RESET_COUNT 6 #define IDX_LOCK_DURATION 7 #define IDX_CHANGE_PASS 8 #define IDX_FORCE_LOGOFF 9 #define IDX_CLEAR_PASS 10 #define IDX_LSA_ANON_LOOKUP 11 #define IDX_ENABLE_ADMIN 12 #define IDX_ENABLE_GUEST 13 static PWSTR LogItems[]={ {(PWSTR)TEXT("MaximumLogSize")}, {(PWSTR)TEXT("AuditLogRetentionPeriod")}, {(PWSTR)TEXT("RetentionDays")}, {(PWSTR)TEXT("RestrictGuestAccess")} }; #define MAX_LOG_ITEMS 4 #define IDX_MAX_LOG_SIZE 0 #define IDX_RET_PERIOD 1 #define IDX_RET_DAYS 2 #define IDX_RESTRICT_GUEST 3 static PWSTR EventItems[]={ {(PWSTR)TEXT("AuditSystemEvents")}, {(PWSTR)TEXT("AuditLogonEvents")}, {(PWSTR)TEXT("AuditObjectAccess")}, {(PWSTR)TEXT("AuditPrivilegeUse")}, {(PWSTR)TEXT("AuditPolicyChange")}, {(PWSTR)TEXT("AuditAccountManage")}, {(PWSTR)TEXT("AuditProcessTracking")}, {(PWSTR)TEXT("AuditDSAccess")}, {(PWSTR)TEXT("AuditAccountLogon")}}; #define MAX_EVENT_ITEMS 9 #define IDX_AUDIT_SYSTEM 0 #define IDX_AUDIT_LOGON 1 #define IDX_AUDIT_OBJECT 2 #define IDX_AUDIT_PRIV 3 #define IDX_AUDIT_POLICY 4 #define IDX_AUDIT_ACCOUNT 5 #define IDX_AUDIT_PROCESS 6 #define IDX_AUDIT_DS 7 #define IDX_AUDIT_ACCT_LOGON 8 // // forward references // SCESTATUS ScepAnalyzeInitialize( IN PCWSTR InfFileName OPTIONAL, IN PWSTR DatabaseName, IN BOOL bAdminLogon, IN AREA_INFORMATION Area, IN DWORD AnalyzeOptions ); SCESTATUS ScepAnalyzeStart( IN AREA_INFORMATION Area, IN BOOL bSystemDb ); NTSTATUS ScepAdminGuestAccountsToManage( IN SAM_HANDLE DomainHandle, IN DWORD AccountType, IN PWSTR TargetName OPTIONAL, OUT PBOOL ToRename, OUT PWSTR *CurrentName OPTIONAL, OUT PDWORD pNameLen OPTIONAL ); SCESTATUS ScepAnalyzePrivileges( IN PSCE_PRIVILEGE_ASSIGNMENT pPrivilegeList ); DWORD ScepGetLSAPolicyObjectInfo( OUT DWORD *pdwAllow ); /* NTSTATUS ScepGetCurrentPrivilegesRights( IN LSA_HANDLE PolicyHandle, IN SAM_HANDLE BuiltinDomainHandle, IN PSID BuiltinDomainSid, IN SAM_HANDLE DomainHandle, IN PSID DomainSid, IN SAM_HANDLE UserHandle OPTIONAL, IN PSID AccountSid, OUT PDWORD PrivilegeRights, OUT PSCE_NAME_STATUS_LIST *pPrivList ); */ SCESTATUS ScepAddAllBuiltinGroups( IN PSCE_GROUP_MEMBERSHIP *pGroupList ); SCESTATUS ScepAnalyzeGroupMembership( IN PSCE_GROUP_MEMBERSHIP pGroupMembership ); NTSTATUS ScepCompareMembersOfGroup( IN SAM_HANDLE DomainHandle, IN PSID ThisDomainSid, IN LSA_HANDLE PolicyHandle, IN SID_NAME_USE GrpUse, IN SAM_HANDLE GroupHandle, IN PSCE_NAME_LIST pChkMembers, OUT PSCE_NAME_LIST *ppMembers, OUT PBOOL bDifferent ); SCESTATUS ScepEnumerateRegistryRoots( OUT PSCE_OBJECT_LIST *pRoots ); SCESTATUS ScepEnumerateFileRoots( OUT PSCE_OBJECT_LIST *pRoots ); SCESTATUS ScepAnalyzeObjectSecurity( IN PSCE_OBJECT_LIST pObjectCheckList, IN AREA_INFORMATION Area, IN BOOL bSystemDb ); DWORD ScepAnalyzeOneObjectInTree( IN PSCE_OBJECT_TREE ThisNode, IN SE_OBJECT_TYPE ObjectType, IN HANDLE Token, IN PGENERIC_MAPPING GenericMapping ); DWORD ScepAnalyzeObjectOnly( IN PWSTR ObjectFullName, IN BOOL IsContainer, IN SE_OBJECT_TYPE ObjectType, IN PSECURITY_DESCRIPTOR ProfileSD, IN SECURITY_INFORMATION ProfileSeInfo ); DWORD ScepGetFileSecurityInfo( IN HANDLE Handle, IN SECURITY_INFORMATION SecurityInfo, OUT PSECURITY_DESCRIPTOR * pSecurityDescriptor ); DWORD ScepGetSecurityDescriptorParts( IN PISECURITY_DESCRIPTOR pSecurityDescriptor, IN SECURITY_INFORMATION SecurityInfo, OUT PSECURITY_DESCRIPTOR *pOutSecurityDescriptor ); DWORD ScepGetKeySecurityInfo( IN HANDLE Handle, IN SECURITY_INFORMATION SecurityInfo, OUT PSECURITY_DESCRIPTOR *pSecurityDescriptor ); DWORD ScepAnalyzeObjectAndChildren( IN PWSTR ObjectFullName, IN SE_OBJECT_TYPE ObjectType, IN PSECURITY_DESCRIPTOR ProfileSD, IN SECURITY_INFORMATION ProfileSeInfo ); SCESTATUS ScepAnalyzeSystemAuditing( IN PSCE_PROFILE_INFO pSmpInfo, IN PPOLICY_AUDIT_EVENTS_INFO auditEvent ); SCESTATUS ScepAnalyzeDeInitialize( IN SCESTATUS rc, IN DWORD Options ); SCESTATUS ScepMigrateDatabaseRevision0( IN PSCECONTEXT cxtProfile ); SCESTATUS ScepMigrateDatabaseRevision1( IN PSCECONTEXT cxtProfile ); SCESTATUS ScepMigrateObjectSection( IN PSCECONTEXT cxtProfile, IN PCWSTR szSection ); SCESTATUS ScepMigrateOneSection( PSCESECTION hSection ); SCESTATUS ScepMigrateLocalTableToTattooTable( IN PSCECONTEXT hProfile ); SCESTATUS ScepMigrateDatabase( IN PSCECONTEXT cxtProfile, IN BOOL bSystemDb ); SCESTATUS ScepDeleteOldRegValuesFromTable( IN PSCECONTEXT hProfile, IN SCETYPE TableType ); BOOL ScepCompareSidNameList( IN PSCE_NAME_LIST pList1, IN PSCE_NAME_LIST pList2 ); DWORD ScepConvertSidListToStringName( IN LSA_HANDLE LsaPolicy, IN OUT PSCE_NAME_LIST pList ); BOOL ScepCompareGroupNameList( IN PUNICODE_STRING DomainName, IN PSCE_NAME_LIST pListToCmp, IN PSCE_NAME_LIST pList ); SCESTATUS ScepGetSystemPrivileges( IN DWORD Options, IN OUT PSCE_ERROR_LOG_INFO *pErrLog, OUT PSCE_PRIVILEGE_ASSIGNMENT *pCurrent ); // // function implementations // SCESTATUS ScepAnalyzeSystem( IN PCWSTR InfFileName OPTIONAL, IN PWSTR DatabaseName, IN DWORD AnalyzeOptions, IN BOOL bAdminLogon, IN AREA_INFORMATION Area, IN PDWORD pdWarning OPTIONAL, IN PWSTR InfRollback OPTIONAL ) /*++ Routine Description: This routine is the exported API to analyze a system and save mismatched/ unknown information to the SAP profile. If any error occurs when loading SMP information into memory, this routine will stop, free memmory, and return the error code. If a error occurs when analyze an area, it will stop analyze. All successful and fail transactions will be logged to the logfile(or stdout). All analysis information is saved to a SAP profile with a date/time stamp. All old analysis information is cleared before new SAP information is saved. Arguments: InfFileName - The file name of a SCP used to compare the analysis of a system DatabaseName - The JET analysis database name. If NULL, default is used. AnalyzeOptions - options to analyze bAdminLogon - if the current calling thread is in administrator's logon Area - One or more areas to configure. AREA_SECURITY_POLICY AREA_USER_SETTINGS AREA_GROUP_MEMBERSHIP AREA_REGISTRY_SECURITY AREA_SYSTEM_SERVICE AREA_FILE_SECURITY pdWarning - the warning level Return value: SCESTATUS_SUCCESS SCESTATUS_NOT_ENOUGH_RESOURCE SCESTATUS_INVALID_PARAMETER SCESTATUS_ALREADY_RUNNING Status from ScepGetDatabaseInfo -- */ { SCESTATUS rc; if ( AnalyzeOptions & SCE_GENERATE_ROLLBACK ) { if ( InfRollback == NULL ) return SCESTATUS_INVALID_PARAMETER; // // check if we can write to the file first // rc = ScepVerifyTemplateName(InfRollback, NULL); if ( rc != ERROR_SUCCESS ) return ScepDosErrorToSceStatus(rc); Area = Area & ( AREA_SECURITY_POLICY | AREA_GROUP_MEMBERSHIP | AREA_PRIVILEGES | AREA_SYSTEM_SERVICE); } rc = ScepAnalyzeInitialize( InfFileName, DatabaseName, bAdminLogon, Area, AnalyzeOptions ); if ( rc != SCESTATUS_SUCCESS ) { ScepLogOutput3(0,0, SCEDLL_SAP_INIT_ERROR); ScepPostProgress(gTotalTicks, 0, NULL); } else { ScepLogOutput3(0,0, SCEDLL_SAP_INIT_SUCCESS); if ( !(AnalyzeOptions & SCE_RE_ANALYZE) && (AnalyzeOptions & SCE_NO_ANALYZE) ) { // && hProfile && // ( (hProfile->Type & 0xF0L) == SCEJET_MERGE_TABLE_1 || // (hProfile->Type & 0xF0L) == SCEJET_MERGE_TABLE_2 ) ) { // // there is "merged" policy table already, do not query any policy // ScepLogOutput3(0, 0, IDS_NO_ANALYSIS); } else { BOOL bSystemDb = FALSE; if ( bAdminLogon && ( AnalyzeOptions & SCE_SYSTEM_DB) ) { bSystemDb = TRUE; } rc = ScepAnalyzeStart( Area, bSystemDb); if ( (AnalyzeOptions & SCE_GENERATE_ROLLBACK) && (SCESTATUS_SUCCESS == rc ) ) { // // export the settings in SMP to the INF file // if ( !WritePrivateProfileSection( L"Version", L"signature=\"$CHICAGO$\"\0Revision=1\0\0", (LPCTSTR)InfRollback) ) { rc = ScepDosErrorToSceStatus(GetLastError()); } else { HINSTANCE hSceCliDll = LoadLibrary(TEXT("scecli.dll")); if ( hSceCliDll ) { PFSCEINFWRITEINFO pfSceInfWriteInfo = (PFSCEINFWRITEINFO)GetProcAddress( hSceCliDll, "SceWriteSecurityProfileInfo"); if ( pfSceInfWriteInfo ) { PSCE_ERROR_LOG_INFO pErrlog=NULL, pErr; PSCE_PROFILE_INFO pSmpInfo=NULL; // // get from database // rc = ScepGetDatabaseInfo( hProfile, SCE_ENGINE_SMP, Area, 0, &pSmpInfo, &pErrlog ); if ( rc == SCESTATUS_SUCCESS && pSmpInfo ) { // // write it into the template // rc = (*pfSceInfWriteInfo) ( InfRollback, Area, pSmpInfo, &pErrlog ); } // // log error // for ( pErr=pErrlog; pErr != NULL; pErr = pErr->next ) { if ( pErr->buffer != NULL ) { ScepLogOutput2(1, pErr->rc, pErr->buffer ); } } // // free buffer // ScepFreeErrorLog(pErrlog); if ( pSmpInfo ) { SceFreeProfileMemory(pSmpInfo); } } else { rc = ScepDosErrorToSceStatus(GetLastError()); } FreeLibrary(hSceCliDll); } else { rc = ScepDosErrorToSceStatus(GetLastError()); } } if ( rc != SCESTATUS_SUCCESS ) { ScepLogOutput3(1, ScepSceStatusToDosError(rc), SCEDLL_ERROR_GENERATE, InfRollback ); } } if ( AnalyzeOptions & SCE_GENERATE_ROLLBACK ) { // // empty local policy table // ScepDeleteInfoForAreas( hProfile, SCE_ENGINE_SMP, AREA_ALL ); } } } ScepLogOutput3(0,0, SCEDLL_SAP_UNINIT); if ( pdWarning ) { *pdWarning = gWarningCode; } // // return failure if invalid data is found in the template // if ( gbInvalidData ) { rc = SCESTATUS_INVALID_DATA; } ScepAnalyzeDeInitialize( rc, AnalyzeOptions ); return(rc); } SCESTATUS ScepAnalyzeStart( IN AREA_INFORMATION Area, IN BOOL bSystemDb ) /* Routine Description: Analyze the system (the real work) Arguments: Area - the security areas to analyze Return Value: SCESTATUS */ { SCESTATUS rc; SCESTATUS Saverc=SCESTATUS_SUCCESS; SCE_PROFILE_INFO SmpInfo; PSCE_PROFILE_INFO pSmpInfo=NULL; PPOLICY_AUDIT_EVENTS_INFO auditEvent=NULL; BOOL bAuditOff=FALSE; // PBYTE pFullAudit = NULL; pSmpInfo = &SmpInfo; memset(pSmpInfo, '\0', sizeof(SCE_PROFILE_INFO)); /* // // 462050 - should not turn off object access auditing to leave a // security hole on the system // // turn off object access auditing if file/key is to be configured // in system context. // if ( (Area & AREA_FILE_SECURITY) || (Area & AREA_REGISTRY_SECURITY) ) bAuditOff = TRUE; // // if set, this regkey will decide to audit all // ScepRegQueryBinaryValue( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\Lsa", L"fullprivilegeauditing", &pFullAudit ); if (pFullAudit) { if (*pFullAudit & (BYTE)1) bAuditOff = FALSE; ScepFree(pFullAudit); } */ rc = ScepSaveAndOffAuditing(&auditEvent, bAuditOff, NULL); // if ( rc != SCESTATUS_SUCCESS ) // goto Done; // if auditing can't be turned on for some reason, e.g., access denied for // normal user, just continue (actually normal user shouldn't turn off auditing // // privileges // if ( Area & AREA_PRIVILEGES ) { ScepPostProgress(0, AREA_PRIVILEGES, NULL); ScepLogOutput3(0,0, SCEDLL_SAP_READ_PROFILE); rc = ScepGetProfileOneArea( hProfile, bSystemDb ? SCE_ENGINE_SCP : SCE_ENGINE_SMP, AREA_PRIVILEGES, SCE_ACCOUNT_SID, &pSmpInfo ); if ( rc == SCESTATUS_SUCCESS ) { ScepLogOutput3(0,0, SCEDLL_SAP_BEGIN_PRIVILEGES); rc = ScepAnalyzePrivileges( pSmpInfo->OtherInfo.smp.pPrivilegeAssignedTo); SceFreeMemory((PVOID)pSmpInfo, AREA_PRIVILEGES ); } else { ScepPostProgress(TICKS_PRIVILEGE, AREA_PRIVILEGES, NULL); } if( rc != SCESTATUS_SUCCESS ) { Saverc = rc; ScepLogOutput3(0,0, SCEDLL_SAP_PRIVILEGES_ERROR); } else { ScepLogOutput3(0,0, SCEDLL_SAP_PRIVILEGES_SUCCESS); } } // // Group membership // if ( ( Area & AREA_GROUP_MEMBERSHIP) && !(gOptions & SCE_NO_ANALYZE) ) { ScepPostProgress(0, AREA_GROUP_MEMBERSHIP, NULL); ScepLogOutput3(0,0, SCEDLL_SAP_READ_PROFILE); rc = ScepGetProfileOneArea( hProfile, bSystemDb ? SCE_ENGINE_SCP : SCE_ENGINE_SMP, AREA_GROUP_MEMBERSHIP, SCE_ACCOUNT_SID, &pSmpInfo ); // need to support nested groups if ( rc == SCESTATUS_SUCCESS ) { ScepLogOutput3(0,0, SCEDLL_SAP_BEGIN_GROUPMGMT); // // Prepare a JET section // rc = ScepStartANewSection( hProfile, &hSection, (gOptions & SCE_GENERATE_ROLLBACK) ? SCEJET_TABLE_SMP : SCEJET_TABLE_SAP, szGroupMembership ); if ( rc == SCESTATUS_SUCCESS ) { // // analyze all builtin groups, what is the baseline for them ? // don't care if errors // if ( !(gOptions & SCE_GENERATE_ROLLBACK) ) { ScepAddAllBuiltinGroups(&(pSmpInfo->pGroupMembership)); } #if _WIN32_WINNT>=0x0500 if ( ProductType == NtProductLanManNt ) { rc = ScepAnalyzeDsGroups( pSmpInfo->pGroupMembership ); // // some groups may not be analyzed by the DS function // so do it again here // SCESTATUS rc2 = ScepAnalyzeGroupMembership( pSmpInfo->pGroupMembership ); if ( SCESTATUS_SUCCESS != rc2 ) rc = rc2; } else { #endif // // workstation or NT4 DCs // rc = ScepAnalyzeGroupMembership( pSmpInfo->pGroupMembership ); #if _WIN32_WINNT>=0x0500 } #endif } else { ScepPostProgress(TICKS_GROUPS, AREA_GROUP_MEMBERSHIP, NULL); ScepLogOutput3(0, ScepSceStatusToDosError(rc), SCEDLL_ERROR_OPEN, (PWSTR)szGroupMembership); } SceFreeMemory((PVOID)pSmpInfo, AREA_GROUP_MEMBERSHIP ); } else { ScepPostProgress(TICKS_GROUPS, AREA_GROUP_MEMBERSHIP, NULL); } if( rc != SCESTATUS_SUCCESS ) { Saverc = rc; ScepLogOutput3(0,0, SCEDLL_SAP_GROUPMGMT_ERROR); } else { ScepLogOutput3(0,0, SCEDLL_SAP_GROUPMGMT_SUCCESS); } } // // Registry Security area // do not support snapshot and rollback // if ( Area & AREA_REGISTRY_SECURITY && !(gOptions & SCE_NO_ANALYZE) && !(gOptions & SCE_GENERATE_ROLLBACK ) ) { ScepPostProgress(0, AREA_REGISTRY_SECURITY, NULL); ScepLogOutput3(0,0, SCEDLL_SAP_READ_PROFILE); rc = ScepGetProfileOneArea( hProfile, bSystemDb ? SCE_ENGINE_SCP : SCE_ENGINE_SMP, AREA_REGISTRY_SECURITY, 0, &pSmpInfo ); if ( rc == SCESTATUS_SUCCESS ) { ScepLogOutput3(0,0, SCEDLL_SAP_BEGIN_REGISTRY); rc = ScepEnumerateRegistryRoots(&(pSmpInfo->pRegistryKeys.pOneLevel)); if ( rc == SCESTATUS_SUCCESS ) { rc = ScepAnalyzeObjectSecurity(pSmpInfo->pRegistryKeys.pOneLevel, AREA_REGISTRY_SECURITY, bSystemDb ); } else { ScepPostProgress(gMaxRegTicks, AREA_REGISTRY_SECURITY, NULL); } SceFreeMemory((PVOID)pSmpInfo, AREA_REGISTRY_SECURITY); } else { ScepPostProgress(gMaxRegTicks, AREA_REGISTRY_SECURITY, NULL); } if( rc != SCESTATUS_SUCCESS ) { Saverc = rc; ScepLogOutput3(0,0, SCEDLL_SAP_REGISTRY_ERROR); } else { ScepLogOutput3(0,0, SCEDLL_SAP_REGISTRY_SUCCESS); } } // // File Security area // do not support snapshot and rollback // if ( Area & AREA_FILE_SECURITY && !(gOptions & SCE_NO_ANALYZE) && !(gOptions & SCE_GENERATE_ROLLBACK)) { ScepPostProgress(0, AREA_FILE_SECURITY, NULL); ScepLogOutput3(0,0, SCEDLL_SAP_READ_PROFILE); rc = ScepGetProfileOneArea( hProfile, bSystemDb ? SCE_ENGINE_SCP : SCE_ENGINE_SMP, AREA_FILE_SECURITY, 0, &pSmpInfo ); if ( rc == SCESTATUS_SUCCESS ) { ScepLogOutput3(0,0, SCEDLL_SAP_BEGIN_FILE); rc = ScepEnumerateFileRoots(&(pSmpInfo->pFiles.pOneLevel)); if ( rc == SCESTATUS_SUCCESS ) { rc = ScepAnalyzeObjectSecurity(pSmpInfo->pFiles.pOneLevel, AREA_FILE_SECURITY, bSystemDb ); } else { ScepPostProgress(gMaxFileTicks, AREA_FILE_SECURITY, NULL); } SceFreeMemory((PVOID)pSmpInfo, AREA_FILE_SECURITY); } else { ScepPostProgress(gMaxFileTicks, AREA_FILE_SECURITY, NULL); } if( rc != SCESTATUS_SUCCESS ) { Saverc = rc; ScepLogOutput3(0,0, SCEDLL_SAP_FILE_ERROR); } else { ScepLogOutput3(0,0, SCEDLL_SAP_FILE_SUCCESS); } } // // System Service area // if ( Area & AREA_SYSTEM_SERVICE && !(gOptions & SCE_NO_ANALYZE) ) { ScepPostProgress(0, AREA_SYSTEM_SERVICE, NULL); ScepLogOutput3(0,0, SCEDLL_SAP_BEGIN_GENERALSVC); rc = ScepAnalyzeGeneralServices( hProfile, gOptions); if( rc != SCESTATUS_SUCCESS ) { Saverc = rc; ScepLogOutput3(0,0, SCEDLL_SAP_GENERALSVC_ERROR); } else { ScepLogOutput3(0,0, SCEDLL_SAP_GENERALSVC_SUCCESS); } if ( !(gOptions & SCE_GENERATE_ROLLBACK) ) { // // attachments // ScepLogOutput3(0,0, SCEDLL_SAP_BEGIN_ATTACHMENT); rc = ScepInvokeSpecificServices( hProfile, FALSE, SCE_ATTACHMENT_SERVICE ); if( rc != SCESTATUS_SUCCESS && SCESTATUS_SERVICE_NOT_SUPPORT != rc ) { Saverc = rc; ScepLogOutput3(0,0, SCEDLL_SAP_ATTACHMENT_ERROR); } else { ScepLogOutput3(0,0, SCEDLL_SAP_ATTACHMENT_SUCCESS); } } } // // System Access area // if ( Area & AREA_SECURITY_POLICY ) { ScepPostProgress(0, AREA_SECURITY_POLICY, NULL); ScepLogOutput3(0,0, SCEDLL_SAP_READ_PROFILE); rc = ScepGetProfileOneArea( hProfile, bSystemDb ? SCE_ENGINE_SCP : SCE_ENGINE_SMP, AREA_SECURITY_POLICY, 0, &pSmpInfo ); if ( rc == SCESTATUS_SUCCESS ) { ScepLogOutput3(0,0, SCEDLL_SAP_BEGIN_POLICY); rc = ScepAnalyzeSystemAccess( pSmpInfo, NULL, 0, NULL, NULL ); // // in setup fresh installed, SAM domain can't be opened // because computer name is changed. In this case // do not log error because nothing needs to be analyzed // if ( !(gOptions & SCE_NO_ANALYZE) || (rc != SCESTATUS_SERVICE_NOT_SUPPORT) ) { if( rc != SCESTATUS_SUCCESS ) { Saverc = rc; ScepLogOutput3(0,0, SCEDLL_SAP_ACCESS_ERROR); } else { ScepLogOutput3(0,0, SCEDLL_SAP_ACCESS_SUCCESS); } } ScepPostProgress(TICKS_SYSTEM_ACCESS, AREA_SECURITY_POLICY, (LPTSTR)szSystemAccess); // // System Auditing area // rc = ScepAnalyzeSystemAuditing( pSmpInfo, auditEvent ); if( rc != SCESTATUS_SUCCESS ) { Saverc = rc; ScepLogOutput3(0,0, SCEDLL_SAP_AUDIT_ERROR); } else { ScepLogOutput3(0,0, SCEDLL_SAP_AUDIT_SUCCESS); } ScepPostProgress(TICKS_SYSTEM_AUDITING, AREA_SECURITY_POLICY, (LPTSTR)szAuditEvent); #if _WIN32_WINNT>=0x0500 if ( ProductType == NtProductLanManNt && !(gOptions & SCE_NO_ANALYZE) ) { // // analyze kerberos policy // rc = ScepAnalyzeKerberosPolicy( hProfile, pSmpInfo->pKerberosInfo, gOptions ); if( rc != SCESTATUS_SUCCESS ) { Saverc = rc; ScepLogOutput3(0,0, SCEDLL_SAP_KERBEROS_ERROR); } else { ScepLogOutput3(0,0, SCEDLL_SAP_KERBEROS_SUCCESS); } } #endif ScepPostProgress(TICKS_KERBEROS, AREA_SECURITY_POLICY, (LPTSTR)szKerberosPolicy); // // analyze registry values // DWORD RegFlag; if ( gOptions & SCE_NO_ANALYZE ) RegFlag = SCEREG_VALUE_SNAPSHOT; else if ( gOptions & SCE_GENERATE_ROLLBACK ) RegFlag = SCEREG_VALUE_ROLLBACK; else RegFlag = SCEREG_VALUE_ANALYZE; rc = ScepAnalyzeRegistryValues( hProfile, RegFlag, pSmpInfo ); if( rc != SCESTATUS_SUCCESS ) { Saverc = rc; ScepLogOutput3(0,0, SCEDLL_SAP_REGVALUES_ERROR); } else { ScepLogOutput3(0,0, SCEDLL_SAP_REGVALUES_SUCCESS); } ScepPostProgress(TICKS_REGISTRY_VALUES, AREA_SECURITY_POLICY, (LPTSTR)szRegistryValues); SceFreeMemory((PVOID)pSmpInfo, AREA_SECURITY_POLICY); } else { ScepPostProgress(TICKS_SECURITY_POLICY_DS, AREA_SECURITY_POLICY, NULL); Saverc = rc; ScepLogOutput3(0,0, SCEDLL_SAP_POLICY_ERROR); } ScepLogOutput3(0,0, SCEDLL_SAP_BEGIN_ATTACHMENT); if ( !(gOptions & SCE_NO_ANALYZE) && !(gOptions & SCE_GENERATE_ROLLBACK) ) { // // attachments // rc = ScepInvokeSpecificServices( hProfile, FALSE, SCE_ATTACHMENT_POLICY ); if( rc != SCESTATUS_SUCCESS && SCESTATUS_SERVICE_NOT_SUPPORT != rc ) { Saverc = rc; ScepLogOutput3(0,0, SCEDLL_SAP_ATTACHMENT_ERROR); } else { ScepLogOutput3(0,0, SCEDLL_SAP_ATTACHMENT_SUCCESS); } } else { ScepPostProgress(TICKS_SPECIFIC_POLICIES, AREA_SECURITY_POLICY, NULL); } } if ( NULL != auditEvent && bAuditOff ) { // // turn auditing back on if it was on // if ( auditEvent->AuditingMode ) rc = ScepRestoreAuditing(auditEvent,NULL); LsaFreeMemory(auditEvent); } return(Saverc); } SCESTATUS ScepAnalyzeInitialize( IN PCWSTR InfFileName OPTIONAL, IN PWSTR DatabaseName, IN BOOL bAdminLogon, IN AREA_INFORMATION Area, IN DWORD AnalyzeOptions ) /* ++ Routine Description: This routine initializes the SAP engine. Arguments: InfFileName - The file name of a SCP file used to compare with the sytem DatabaseName - The Jet analysis database name bAdminLogon - if administrator logs on AnalyzeOptions - The flag to indicate if the template should be appended to the existing database if one exists Area - security area to analyze Return value: SCESTATUS_SUCCESS SCESTATUS_INVALID_PARAMETER SCESTATUS_PROFILE_NOT_FOUND SCESTATUS_NOT_ENOUGH_RESOURCE SCESTATUS_ALREADY_RUNNING -- */ { SCESTATUS rc=SCESTATUS_SUCCESS; PCHAR FileName=NULL; DWORD MBLen=0; NTSTATUS NtStatus; DWORD NameLen=MAX_COMPUTERNAME_LENGTH; PSCE_ERROR_LOG_INFO Errlog=NULL; DWORD SCPLen=0, DefLen=0; PWSTR BaseProfile=NULL; PSECURITY_DESCRIPTOR pSD=NULL; SECURITY_INFORMATION SeInfo; DWORD SDsize; SCEJET_CREATE_TYPE DbFlag; HKEY hCurrentUser=NULL; // // database name can't be NULL because it's already resolved // if ( !DatabaseName ) { return(SCESTATUS_INVALID_PARAMETER); } BOOL bSetupDb = (bAdminLogon && (AnalyzeOptions & SCE_NO_ANALYZE) && (AnalyzeOptions & SCE_SYSTEM_DB)); // // get other system values // if ( RtlGetNtProductType (&ProductType) == FALSE ) { rc = ScepDosErrorToSceStatus(GetLastError()); goto Leave; } gTotalTicks = 4*TICKS_MIGRATION_SECTION+TICKS_MIGRATION_V11; gCurrentTicks = 0; gWarningCode = 0; gbInvalidData = FALSE; cbClientFlag = (BYTE)(AnalyzeOptions & (SCE_CALLBACK_DELTA | SCE_CALLBACK_TOTAL)); DefLen = wcslen(DatabaseName); if ( InfFileName != NULL ) { SCPLen = wcslen(InfFileName); } // // Open the file // NtStatus = RtlUnicodeToMultiByteSize(&MBLen, DatabaseName, DefLen*sizeof(WCHAR)); if ( !NT_SUCCESS(NtStatus) ) { // // cannot get the length, set default to 512 // MBLen = 512; } FileName = (PCHAR)ScepAlloc(LPTR, MBLen+2); if ( FileName == NULL ) { rc = SCESTATUS_NOT_ENOUGH_RESOURCE; goto Leave; } NtStatus = RtlUnicodeToMultiByteN( FileName, MBLen+1, NULL, DatabaseName, DefLen*sizeof(WCHAR) ); if ( !NT_SUCCESS(NtStatus) ) { ScepLogOutput3(3, RtlNtStatusToDosError(NtStatus), SCEDLL_ERROR_PROCESS_UNICODE, DatabaseName ); rc = ScepDosErrorToSceStatus( RtlNtStatusToDosError(NtStatus) ); goto Leave; } // // if the tattoo table doesn't exist yet, the call will still return success // rc = SceJetOpenFile( (LPSTR)FileName, (AnalyzeOptions & SCE_GENERATE_ROLLBACK) ? SCEJET_OPEN_READ_WRITE : SCEJET_OPEN_EXCLUSIVE, bSetupDb ? SCE_TABLE_OPTION_TATTOO : 0, &hProfile ); // // if database exist with wrong format, migrate it // if it's the system database, delete everything since info there is not needed // migrate the database // SDsize = 0; if ( (SCESTATUS_BAD_FORMAT == rc) && !(AnalyzeOptions & SCE_GENERATE_ROLLBACK) ) { // // should this in transaction ??? // rc = SceJetOpenFile( (LPSTR)FileName, SCEJET_OPEN_NOCHECK_VERSION, 0, &hProfile ); if ( SCESTATUS_SUCCESS == rc ) { rc = ScepMigrateDatabase( hProfile, bSetupDb ); if ( rc != SCESTATUS_BAD_FORMAT ) { // // old or current version, migrated // if ( rc != SCESTATUS_SUCCESS ) { ScepLogOutput3(1, ScepSceStatusToDosError(rc), SCEDLL_ERROR_CONVERT, DatabaseName); } SDsize = 1; } // else newer version, not migrated } else { ScepLogOutput3(1, ScepSceStatusToDosError(rc), SCEDLL_ERROR_OPEN, DatabaseName); rc = SCESTATUS_BAD_FORMAT; } if ( SDsize == 0 ) { ScepPostProgress(4*TICKS_MIGRATION_SECTION+TICKS_MIGRATION_V11, 0, NULL); } if ( rc != SCESTATUS_SUCCESS ) { goto Leave; } } else if ( SCESTATUS_SUCCESS == rc && bSetupDb ) { // // for system database, check to see if the database contain tattoo table // if ( hProfile->JetSapID != JET_tableidNil ) { // // if the tattoo table already exists, this is the latest database // nothing needs to be done to migrate // but we need to make sure that for domain controllers, the tattoo // table does not contain filtered policies (user rights, account policies) // if ( ProductType == NtProductLanManNt ) { // // empty tattoo policy settings // ScepDeleteInfoForAreas( hProfile, SCE_ENGINE_SAP, // tattoo table AREA_PRIVILEGES ); // // delete szSystemAccess section info // ScepDeleteOneSection( hProfile, SCE_ENGINE_SAP, szSystemAccess ); // // delete szAuditEvent section info // ScepDeleteOneSection( hProfile, SCE_ENGINE_SAP, szAuditEvent ); // // delete szKerberosPolicy section info // ScepDeleteOneSection( hProfile, SCE_ENGINE_SAP, szKerberosPolicy ); ScepLogOutput2(0, 0, L"Empty tattoo table on domain controllers"); } } else { // // create the tattoo table and move data from local table to the tattoo table // for the settings defined in merged policy // rc = SceJetCreateTable( hProfile, "SmTblTattoo", SCEJET_TABLE_TATTOO, SCEJET_CREATE_IN_BUFFER, NULL, NULL ); if ( SCESTATUS_SUCCESS == rc && ( (hProfile->Type & 0xF0L) == SCEJET_MERGE_TABLE_1 || (hProfile->Type & 0xF0L) == SCEJET_MERGE_TABLE_2 ) ) { // // effective policy table exist // migrate local policy to tattoo table // do not care errors // note, on domain controllers, user rights are not migrated // ScepMigrateLocalTableToTattooTable(hProfile); ScepLogOutput2(0, 0, L"Migrate local table to tattoo table"); } } // // empty local policy settings // if ( SCESTATUS_SUCCESS == rc ) ScepDeleteInfoForAreas( hProfile, SCE_ENGINE_SMP, AREA_ALL ); ScepPostProgress(4*TICKS_MIGRATION_SECTION+TICKS_MIGRATION_V11, 0, NULL); } else { ScepPostProgress(4*TICKS_MIGRATION_SECTION+TICKS_MIGRATION_V11, 0, NULL); } // // Logic to determine what to do with the template // // if InfFileName provided // if Jet database exist (either the default, or the DatabaseName) // // if append flag is on // append the template on top of the database then continue to analyze // // else if DatabaseName provided // overwrite the database then continue to analyze // else // log an error to ignore the template, then continue to analyze // else // overwrite the database then continue to analyze // else // if Jet database exist // continue to analyze // else if query system settings // create the database then query settings // else // error out // // // the HKEY_CURRENT_USER may be linked to .default // depends on the current calling process // if ( RegOpenCurrentUser( KEY_READ | KEY_WRITE, &hCurrentUser ) != ERROR_SUCCESS ) { hCurrentUser = NULL; } if ( hCurrentUser == NULL ) { hCurrentUser = HKEY_CURRENT_USER; } if ( rc == SCESTATUS_SUCCESS && (SCPLen <= 0 || ((AnalyzeOptions & SCE_OVERWRITE_DB) && (AnalyzeOptions & SCE_SYSTEM_DB))) ) { // // database exists with no template or // database exists with template but template is overwriting to the existing database // if ( SCPLen > 0 && (AnalyzeOptions & SCE_OVERWRITE_DB) && (AnalyzeOptions & SCE_SYSTEM_DB)) { ScepLogOutput3(0,0, SCEDLL_SAP_IGNORE_TEMPLATE); } // // continue the analysis // } else { if ( rc != SCESTATUS_SUCCESS && SCPLen <= 0 ) { // // database does not exist, and template is not provided // if SCE_NO_ANALYZE is provided, query the system, // otherwise, error out, rc is the error, will be logged later if ( AnalyzeOptions & SCE_NO_ANALYZE ) { if ( DatabaseName != NULL && DefLen > 0 ) { rc = ConvertTextSecurityDescriptor ( L"D:P(A;CIOI;GA;;;CO)(A;CIOI;GA;;;BA)(A;CIOI;GA;;;SY)", &pSD, &SDsize, &SeInfo ); if ( rc != NO_ERROR ) ScepLogOutput3(1, rc, SCEDLL_ERROR_BUILD_SD, DatabaseName ); ScepChangeAclRevision(pSD, ACL_REVISION); ScepCreateDirectory( DatabaseName, FALSE, // a file name pSD // take parent's security setting ); if ( pSD ) ScepFree(pSD); } rc = SceJetCreateFile( (LPSTR)FileName, SCEJET_OPEN_DUP_EXCLUSIVE, SCE_TABLE_OPTION_TATTOO, &hProfile ); } } else { if ( rc == SCESTATUS_SUCCESS && SCPLen > 0 && (AnalyzeOptions & SCE_UPDATE_DB ) ) { // // database exist, template provided to append // DbFlag = SCEJET_OPEN_DUP_EXCLUSIVE; } else if ( AnalyzeOptions & SCE_GENERATE_ROLLBACK ) { DbFlag = SCEJET_OPEN_DUP; } else { DbFlag = SCEJET_OVERWRITE_DUP; } // // database exist and opened, will be closed when calling CreateFile // // // InfFileName must exist when gets here. // ScepLogOutput3(3, 0, SCEDLL_PROCESS_TEMPLATE, (PWSTR)InfFileName ); // // make sure the directories exist for the file // if ( DatabaseName != NULL && DefLen > 0 ) { rc = ConvertTextSecurityDescriptor ( L"D:P(A;CIOI;GA;;;CO)(A;CIOI;GA;;;BA)(A;CIOI;GA;;;SY)", &pSD, &SDsize, &SeInfo ); if ( rc != NO_ERROR ) ScepLogOutput3(1, rc, SCEDLL_ERROR_BUILD_SD, DatabaseName ); ScepChangeAclRevision(pSD, ACL_REVISION); ScepCreateDirectory( DatabaseName, FALSE, // a file name pSD // take parent's security setting ); if ( pSD ) ScepFree(pSD); } // // close the database // SceJetCloseFile( hProfile, FALSE, FALSE ); rc = SceJetConvertInfToJet( InfFileName, (LPSTR)FileName, DbFlag, bAdminLogon ? AnalyzeOptions : (AnalyzeOptions & ~SCE_SYSTEM_DB), Area ); if ( rc != SCESTATUS_SUCCESS ) { goto Leave; } rc = SceJetOpenFile( (LPSTR)FileName, (AnalyzeOptions & SCE_GENERATE_ROLLBACK) ? SCEJET_OPEN_READ_WRITE : SCEJET_OPEN_EXCLUSIVE, 0, &hProfile ); if ( rc == SCESTATUS_SUCCESS ) { rc = ScepRegSetValue( bAdminLogon ? HKEY_LOCAL_MACHINE : hCurrentUser, SCE_ROOT_PATH, L"TemplateUsed", REG_SZ, (BYTE *)InfFileName, SCPLen*sizeof(WCHAR) ); if ( rc != NO_ERROR ) // Win32 error code ScepLogOutput3(1, rc, SCEDLL_ERROR_SAVE_REGISTRY, L"TemplateUsed"); rc = SCESTATUS_SUCCESS; } } if ( rc != SCESTATUS_SUCCESS ) { ScepLogOutput3(1, ScepSceStatusToDosError(rc), SCEDLL_ERROR_OPEN, DatabaseName); goto Leave; } } rc = ScepRegSetValue( bAdminLogon ? HKEY_LOCAL_MACHINE : hCurrentUser, SCE_ROOT_PATH, L"LastUsedDatabase", REG_SZ, (BYTE *)DatabaseName, DefLen*sizeof(WCHAR) ); if ( rc != NO_ERROR ) ScepLogOutput3(1, rc, SCEDLL_ERROR_SAVE_REGISTRY, L"LastUsedDatabase"); // // query the total ticks of this analysis // rc = ScepGetTotalTicks( NULL, hProfile, Area, SCE_FLAG_ANALYZE, &gTotalTicks ); if ( SCESTATUS_SUCCESS != rc && SCESTATUS_RECORD_NOT_FOUND != rc ) { ScepLogOutput3(1, ScepSceStatusToDosError(rc), SCEDLL_TOTAL_TICKS_ERROR); } gTotalTicks += (4*TICKS_MIGRATION_SECTION+TICKS_MIGRATION_V11); // // start a new SAP table. // Note here the backup SAP is controlled by the transaction // /* // Maximal version store is 64K by default. rc = SceJetStartTransaction( hProfile ); if ( rc != SCESTATUS_SUCCESS ) { ScepLogOutput2(0, 0, L"Cannot start a transaction"); goto Leave; } */ gOptions = AnalyzeOptions; if ( !(AnalyzeOptions & SCE_NO_ANALYZE) && !(AnalyzeOptions & SCE_GENERATE_ROLLBACK) ) { rc = SceJetDeleteTable( hProfile, "SmTblSap", SCEJET_TABLE_SAP ); if ( SCESTATUS_ACCESS_DENIED != rc ) { rc = SceJetCreateTable( hProfile, "SmTblSap", SCEJET_TABLE_SAP, SCEJET_CREATE_IN_BUFFER, NULL, NULL ); } if ( rc != SCESTATUS_SUCCESS ) { ScepLogOutput3(1, ScepSceStatusToDosError(rc), SCEDLL_ERROR_CREATE, L"SAP."); goto Leave; } } ScepIsDomainLocal(NULL); Leave: if ( hCurrentUser && hCurrentUser != HKEY_CURRENT_USER ) { RegCloseKey(hCurrentUser); } if ( FileName ) { ScepFree(FileName); } return(rc); } SCESTATUS ScepMigrateLocalTableToTattooTable( IN PSCECONTEXT hProfile ) /* Copy settings from local policy table to tattoo policy table if the settings exist in effective table. on domain controllers, privileges are not migrated because we do not want to handle the tattoo problem for domain controllers. hProfile is opened with tattoo table. */ { // // delete old registry values from SMP table (because they are // moved to new location). Don't care error // ScepDeleteOldRegValuesFromTable( hProfile, SCE_ENGINE_SMP ); ScepDeleteOldRegValuesFromTable( hProfile, SCE_ENGINE_SCP ); SCESTATUS rc=SCESTATUS_SUCCESS; // // now move the table // PSCE_ERROR_LOG_INFO Errlog=NULL; rc = ScepCopyLocalToMergeTable(hProfile, 0, SCE_LOCAL_POLICY_MIGRATE | ((ProductType == NtProductLanManNt) ? SCE_LOCAL_POLICY_DC : 0), &Errlog ); ScepLogWriteError( Errlog,1 ); ScepFreeErrorLog( Errlog ); Errlog = NULL; if ( rc != SCESTATUS_SUCCESS ) ScepLogOutput2(1,ScepSceStatusToDosError(rc),L"Error occurred in migration"); return(rc); } SCESTATUS ScepAnalyzeSystemAccess( IN OUT PSCE_PROFILE_INFO pSmpInfo, IN PSCE_PROFILE_INFO pScpInfo OPTIONAL, IN DWORD dwSaveOption, OUT BOOL *pbChanged, IN OUT PSCE_ERROR_LOG_INFO *pErrLog ) /* ++ Routine Description: This routine analyzies the system security in the area of system access which includes account policy, rename admin/guest accounts, and some registry values. Arguments: pSmpInfo - The buffer which contains SMP information to compare with If dwSaveOption is set to non zero, the difference is saved in this buffer to output pScpInfo - If dwSaveOption is set to non zero, pScpInfo may be present to be used (as effective policy) to compare with dwSaveOption - used by policy filter when to query policy difference SCEPOL_SYSTEM_SETTINGS - query for system setting mode SCEPOL_SAVE_DB - policy filter for local database SCEPOL_SAVE_BUFFER - policy filter for GPO mode (DC) 0 - analyze mode pbChanged - TRUE if there is any policy changed Return value: SCESTATUS_SUCCESS SCESTATUS_NOT_ENOUGH_RESOURCE SCESTATUS_INVALID_PARAMETER -- */ { SCESTATUS rc=SCESTATUS_SUCCESS; SCESTATUS saveRc=rc; NTSTATUS NtStatus; SAM_HANDLE DomainHandle=NULL, ServerHandle=NULL, UserHandle1=NULL; PSID DomainSid=NULL; PVOID Buffer=NULL; DWORD BaseVal; PWSTR RegBuf=NULL; DWORD CurrentVal; BOOL ToRename=FALSE; DWORD dwAllow = 0; DWORD AccessValues[MAX_ACCESS_ITEMS]; for ( CurrentVal=0; CurrentValMinPasswordLength; BaseVal = pTmpInfo->MinimumPasswordLength; if ( dwSaveOption & SCEPOL_SYSTEM_SETTINGS ) { pSmpInfo->MinimumPasswordLength = CurrentVal; } else if ( dwSaveOption ) { if ( ( CurrentVal != BaseVal ) && ( BaseVal != SCE_NO_VALUE) ) { pSmpInfo->MinimumPasswordLength = CurrentVal; *pbChanged = TRUE; } else if ( dwSaveOption & SCEPOL_SAVE_DB ) { // // turn this item off to indicate this one is not changed // pSmpInfo->MinimumPasswordLength = SCE_NO_VALUE; } } else { rc = ScepCompareAndSaveIntValue( hSection, L"MinimumPasswordLength", (gOptions & SCE_GENERATE_ROLLBACK), (gOptions & SCE_NO_ANALYZE) ? SCE_SNAPSHOT_VALUE : BaseVal, CurrentVal); if ( rc == SCESTATUS_SUCCESS && !(gOptions & SCE_NO_ANALYZE) ) { AccessValues[IDX_MIN_PASS_LEN] = CurrentVal; } } if ( rc == SCESTATUS_SUCCESS ) { CurrentVal = ((DOMAIN_PASSWORD_INFORMATION *)Buffer)->PasswordHistoryLength; BaseVal = pTmpInfo->PasswordHistorySize; if ( dwSaveOption & SCEPOL_SYSTEM_SETTINGS ) { pSmpInfo->PasswordHistorySize = CurrentVal; } else if ( dwSaveOption ) { if ( ( CurrentVal != BaseVal ) && ( BaseVal != SCE_NO_VALUE) ) { pSmpInfo->PasswordHistorySize = CurrentVal; *pbChanged = TRUE; } else if ( dwSaveOption & SCEPOL_SAVE_DB ) { // // turn this item off to indicate this one is not changed // pSmpInfo->PasswordHistorySize = SCE_NO_VALUE; } } else { rc = ScepCompareAndSaveIntValue( hSection, L"PasswordHistorySize", (gOptions & SCE_GENERATE_ROLLBACK), (gOptions & SCE_NO_ANALYZE) ? SCE_SNAPSHOT_VALUE : BaseVal, CurrentVal ); if ( rc == SCESTATUS_SUCCESS && !(gOptions & SCE_NO_ANALYZE) ) { AccessValues[IDX_PASS_HISTORY] = CurrentVal; } } if ( rc == SCESTATUS_SUCCESS ) { if ( (gOptions & SCE_GENERATE_ROLLBACK) && (pTmpInfo->MaximumPasswordAge == SCE_NO_VALUE) && (pTmpInfo->MinimumPasswordAge == SCE_NO_VALUE) ) { // // generate rollback template. These two are not defined // no need to query/compare // } else { if ( ((DOMAIN_PASSWORD_INFORMATION *)Buffer)->MaxPasswordAge.HighPart == MINLONG && ((DOMAIN_PASSWORD_INFORMATION *)Buffer)->MaxPasswordAge.LowPart == 0 ) { // // Maximum password age value is MINLONG,0 // CurrentVal = SCE_FOREVER_VALUE; } else { CurrentVal = (DWORD) (-1 * (((DOMAIN_PASSWORD_INFORMATION *)Buffer)->MaxPasswordAge.QuadPart / (LONGLONG)(10000000L)) ); CurrentVal /= 3600; CurrentVal /= 24; } BaseVal = pTmpInfo->MaximumPasswordAge; if ( dwSaveOption & SCEPOL_SYSTEM_SETTINGS ) { pSmpInfo->MaximumPasswordAge = CurrentVal; } else if ( dwSaveOption ) { if ( ( CurrentVal != BaseVal ) && ( BaseVal != SCE_NO_VALUE) ) { pSmpInfo->MaximumPasswordAge = CurrentVal; *pbChanged = TRUE; } else if ( dwSaveOption & SCEPOL_SAVE_DB ) { // // turn this item off to indicate this one is not changed // pSmpInfo->MaximumPasswordAge = SCE_NO_VALUE; } } else { rc = ScepCompareAndSaveIntValue( hSection, L"MaximumPasswordAge", FALSE, (gOptions & SCE_NO_ANALYZE) ? SCE_SNAPSHOT_VALUE : BaseVal, CurrentVal); if ( rc == SCESTATUS_SUCCESS && !(gOptions & SCE_NO_ANALYZE) ) { AccessValues[IDX_MAX_PASS_AGE] = CurrentVal; } } if ( rc == SCESTATUS_SUCCESS ) { CurrentVal = (DWORD) (-1 * (((DOMAIN_PASSWORD_INFORMATION *)Buffer)->MinPasswordAge.QuadPart / (LONGLONG)(10000000L)) ); CurrentVal /= 3600; CurrentVal /= 24; BaseVal = pTmpInfo->MinimumPasswordAge; if ( dwSaveOption & SCEPOL_SYSTEM_SETTINGS ) { pSmpInfo->MinimumPasswordAge = CurrentVal; } else if ( dwSaveOption ) { if ( ( CurrentVal != BaseVal ) && ( BaseVal != SCE_NO_VALUE) ) { pSmpInfo->MinimumPasswordAge = CurrentVal; *pbChanged = TRUE; } else if ( dwSaveOption & SCEPOL_SAVE_DB ) { // // turn this item off to indicate this one is not changed // pSmpInfo->MinimumPasswordAge = SCE_NO_VALUE; } } else { rc = ScepCompareAndSaveIntValue( hSection, L"MinimumPasswordAge", FALSE, (gOptions & SCE_NO_ANALYZE) ? SCE_SNAPSHOT_VALUE : BaseVal, CurrentVal); if ( rc == SCESTATUS_SUCCESS && !(gOptions & SCE_NO_ANALYZE) ) { AccessValues[IDX_MIN_PASS_AGE] = CurrentVal; } } } } if ( rc == SCESTATUS_SUCCESS ) { // // Password Complexity // if ( ((DOMAIN_PASSWORD_INFORMATION *)Buffer)->PasswordProperties & DOMAIN_PASSWORD_COMPLEX ) CurrentVal = 1; else CurrentVal = 0; BaseVal = pTmpInfo->PasswordComplexity; if ( dwSaveOption & SCEPOL_SYSTEM_SETTINGS ) { pSmpInfo->PasswordComplexity = CurrentVal; } else if ( dwSaveOption ) { if ( ( CurrentVal != BaseVal ) && ( BaseVal != SCE_NO_VALUE) ) { pSmpInfo->PasswordComplexity = CurrentVal; *pbChanged = TRUE; } else if ( dwSaveOption & SCEPOL_SAVE_DB ) { // // turn this item off to indicate this one is not changed // pSmpInfo->PasswordComplexity = SCE_NO_VALUE; } } else { rc = ScepCompareAndSaveIntValue( hSection, L"PasswordComplexity", (gOptions & SCE_GENERATE_ROLLBACK), (gOptions & SCE_NO_ANALYZE) ? SCE_SNAPSHOT_VALUE : BaseVal, CurrentVal); if ( rc == SCESTATUS_SUCCESS && !(gOptions & SCE_NO_ANALYZE) ) { AccessValues[IDX_PASS_COMPLEX] = CurrentVal; } } if ( rc == SCESTATUS_SUCCESS ) { // // RequireLogonToChangePassword // if ( ((DOMAIN_PASSWORD_INFORMATION *)Buffer)->PasswordProperties & DOMAIN_PASSWORD_NO_ANON_CHANGE ) CurrentVal = 1; else CurrentVal = 0; BaseVal = pTmpInfo->RequireLogonToChangePassword; if ( dwSaveOption & SCEPOL_SYSTEM_SETTINGS ) { pSmpInfo->RequireLogonToChangePassword = CurrentVal; } else if ( dwSaveOption ) { if ( ( CurrentVal != BaseVal ) && ( BaseVal != SCE_NO_VALUE) ) { pSmpInfo->RequireLogonToChangePassword = CurrentVal; *pbChanged = TRUE; } else if ( dwSaveOption & SCEPOL_SAVE_DB ) { // // turn this item off to indicate this one is not changed // pSmpInfo->RequireLogonToChangePassword = SCE_NO_VALUE; } } else { rc = ScepCompareAndSaveIntValue( hSection, L"RequireLogonToChangePassword", (gOptions & SCE_GENERATE_ROLLBACK), (gOptions & SCE_NO_ANALYZE) ? SCE_SNAPSHOT_VALUE : BaseVal, CurrentVal); if ( rc == SCESTATUS_SUCCESS && !(gOptions & SCE_NO_ANALYZE) ) { AccessValues[IDX_CHANGE_PASS] = CurrentVal; } } #if _WIN32_WINNT>=0x0500 if ( rc == SCESTATUS_SUCCESS ) { // // Clear Text Password // CurrentVal = 0; if ( ( (ProductType == NtProductLanManNt) || (ProductType == NtProductServer ) ) && (gOptions & SCE_NO_ANALYZE) ) { // // NT4 DC upgrade, check the registry value // CurrentVal = 0; rc = ScepRegQueryIntValue( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\Lsa\\MD5-CHAP", L"Store Cleartext Passwords", &CurrentVal ); if ( rc != SCESTATUS_SUCCESS ) { CurrentVal = 0; rc = SCESTATUS_SUCCESS; } } if ( CurrentVal == 0 ) { // if not NT4 DC upgrade, or clear text password disabled on NT4 if ( ((DOMAIN_PASSWORD_INFORMATION *)Buffer)->PasswordProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT ) { CurrentVal = 1; } else { CurrentVal = 0; } } BaseVal = pTmpInfo->ClearTextPassword; if ( dwSaveOption & SCEPOL_SYSTEM_SETTINGS ) { pSmpInfo->ClearTextPassword = CurrentVal; } else if ( dwSaveOption ) { if ( ( CurrentVal != BaseVal ) && ( BaseVal != SCE_NO_VALUE) ) { pSmpInfo->ClearTextPassword = CurrentVal; *pbChanged = TRUE; } else if ( dwSaveOption & SCEPOL_SAVE_DB ) { // // turn this item off to indicate this one is not changed // pSmpInfo->ClearTextPassword = SCE_NO_VALUE; } } else { rc = ScepCompareAndSaveIntValue( hSection, L"ClearTextPassword", (gOptions & SCE_GENERATE_ROLLBACK), (gOptions & SCE_NO_ANALYZE) ? SCE_SNAPSHOT_VALUE : BaseVal, CurrentVal); if ( rc == SCESTATUS_SUCCESS && !(gOptions & SCE_NO_ANALYZE) ) { AccessValues[IDX_CLEAR_PASS] = CurrentVal; } } } #else AccessValues[IDX_CLEAR_PASS] = 1; #endif } } } } SamFreeMemory(Buffer); if ( rc != SCESTATUS_SUCCESS ) { saveRc = rc; ScepLogOutput3(1, ScepSceStatusToDosError(rc), SCEDLL_SAP_ERROR_PASSWORD); if ( !(dwSaveOption & SCEPOL_SYSTEM_SETTINGS) ) { // // if it's not getting system setting, quit on error // goto GETOUT; } } else ScepLogOutput3(1, 0, SCEDLL_SAP_PASSWORD ); } else { saveRc = ScepDosErrorToSceStatus(rc); ScepLogOutput3(1,rc, SCEDLL_ERROR_QUERY_PASSWORD); // ntstatus if ( !(dwSaveOption & SCEPOL_SYSTEM_SETTINGS) ) { // // if it's not getting system setting, quit on error // goto GETOUT; } } if ( pErrLog && (saveRc != SCESTATUS_SUCCESS) ) { // // password policy failed. // ScepBuildErrorLogInfo( ScepSceStatusToDosError(saveRc), pErrLog, SCEDLL_ERROR_QUERY_PASSWORD ); } // // Analyze Lockout information // if ( (gOptions & SCE_GENERATE_ROLLBACK) && (pTmpInfo->LockoutBadCount == SCE_NO_VALUE) && (pTmpInfo->ResetLockoutCount == SCE_NO_VALUE) && (pTmpInfo->LockoutDuration == SCE_NO_VALUE) ) { rc = NtStatus = STATUS_SUCCESS; } else { Buffer = NULL; NtStatus = SamQueryInformationDomain( DomainHandle, DomainLockoutInformation, &Buffer ); rc = RtlNtStatusToDosError( NtStatus ); if ( NT_SUCCESS(NtStatus) ) { rc = SCESTATUS_SUCCESS; CurrentVal = ((DOMAIN_LOCKOUT_INFORMATION *)Buffer)->LockoutThreshold; BaseVal = pTmpInfo->LockoutBadCount; DWORD dwLockOut = BaseVal; if ( dwSaveOption & SCEPOL_SYSTEM_SETTINGS ) { // // system setting // pSmpInfo->LockoutBadCount = CurrentVal; } else if ( dwSaveOption ) { // // policy filter mode // if ( CurrentVal != BaseVal && BaseVal != SCE_NO_VALUE ) { pSmpInfo->LockoutBadCount = CurrentVal; *pbChanged = TRUE; if ( CurrentVal == 0 ) { // // if no lockout is allowed, // make sure to delete the entries below // pSmpInfo->ResetLockoutCount = SCE_NO_VALUE; pSmpInfo->LockoutDuration = SCE_NO_VALUE; } } else if ( dwSaveOption & SCEPOL_SAVE_DB ) { // // turn this item off to indicate this one is not changed // pSmpInfo->LockoutBadCount = SCE_NO_VALUE; } } else { // // analyze // rc = ScepCompareAndSaveIntValue( hSection, L"LockoutBadCount", FALSE, (gOptions & SCE_NO_ANALYZE) ? SCE_SNAPSHOT_VALUE : BaseVal, CurrentVal); if ( rc == SCESTATUS_SUCCESS && !(gOptions & SCE_NO_ANALYZE) ) { AccessValues[IDX_LOCK_COUNT] = CurrentVal; } } if ( rc == SCESTATUS_SUCCESS && ((DOMAIN_LOCKOUT_INFORMATION *)Buffer)->LockoutThreshold > 0 ) { CurrentVal = (DWORD) (-1 * ((DOMAIN_LOCKOUT_INFORMATION *)Buffer)->LockoutObservationWindow.QuadPart / (60 * 10000000L) ); BaseVal = pTmpInfo->ResetLockoutCount; if ( dwSaveOption & SCEPOL_SYSTEM_SETTINGS ) { pSmpInfo->ResetLockoutCount = CurrentVal; } else if ( dwSaveOption ) { // // if this setting is defined or LockoutBadCount is defined // filter this value // if ( CurrentVal != BaseVal && (BaseVal != SCE_NO_VALUE || dwLockOut != SCE_NO_VALUE) ) { pSmpInfo->ResetLockoutCount = CurrentVal; *pbChanged = TRUE; } else if ( dwSaveOption & SCEPOL_SAVE_DB ) { // // turn this item off to indicate this one is not changed // pSmpInfo->ResetLockoutCount = SCE_NO_VALUE; } } else { rc = ScepCompareAndSaveIntValue( hSection, L"ResetLockoutCount", FALSE, (gOptions & SCE_NO_ANALYZE) ? SCE_SNAPSHOT_VALUE : BaseVal, CurrentVal); if ( rc == SCESTATUS_SUCCESS && !(gOptions & SCE_NO_ANALYZE) ) { AccessValues[IDX_RESET_COUNT] = CurrentVal; } } if ( rc == SCESTATUS_SUCCESS ) { if ( ((DOMAIN_LOCKOUT_INFORMATION *)Buffer)->LockoutDuration.HighPart == MINLONG && ((DOMAIN_LOCKOUT_INFORMATION *)Buffer)->LockoutDuration.LowPart == 0 ) { // // forever // CurrentVal = SCE_FOREVER_VALUE; } else CurrentVal = (DWORD)(-1 * ((DOMAIN_LOCKOUT_INFORMATION *)Buffer)->LockoutDuration.QuadPart / (60 * 10000000L) ); BaseVal = pTmpInfo->LockoutDuration; if ( dwSaveOption & SCEPOL_SYSTEM_SETTINGS ) { pSmpInfo->LockoutDuration = CurrentVal; } else if ( dwSaveOption ) { // // if this setting is defined or LockoutBadCount is defined // filter this value // if ( CurrentVal != BaseVal && (BaseVal != SCE_NO_VALUE || dwLockOut != SCE_NO_VALUE) ) { pSmpInfo->LockoutDuration = CurrentVal; *pbChanged = TRUE; } else if ( dwSaveOption & SCEPOL_SAVE_DB ) { // // turn this item off to indicate this one is not changed // pSmpInfo->LockoutDuration = SCE_NO_VALUE; } } else { rc = ScepCompareAndSaveIntValue( hSection, L"LockoutDuration", FALSE, (gOptions & SCE_NO_ANALYZE) ? SCE_SNAPSHOT_VALUE : BaseVal, CurrentVal); if ( rc == SCESTATUS_SUCCESS && !(gOptions & SCE_NO_ANALYZE) ) { AccessValues[IDX_LOCK_DURATION] = CurrentVal; } } } } else { AccessValues[IDX_RESET_COUNT] = SCE_NOT_ANALYZED_VALUE; AccessValues[IDX_LOCK_DURATION] = SCE_NOT_ANALYZED_VALUE; } SamFreeMemory(Buffer); if ( rc != SCESTATUS_SUCCESS ) { saveRc = rc; ScepLogOutput3(1, ScepSceStatusToDosError(rc), SCEDLL_SAP_ERROR_LOCKOUT); if ( !(dwSaveOption & SCEPOL_SYSTEM_SETTINGS) ) { goto GETOUT; } } else ScepLogOutput3(1, 0, SCEDLL_SAP_LOCKOUT ); } else { saveRc = ScepDosErrorToSceStatus(rc); ScepLogOutput3(1,rc, SCEDLL_ERROR_QUERY_LOCKOUT); // ntstatus if ( !(dwSaveOption & SCEPOL_SYSTEM_SETTINGS) ) { goto GETOUT; } } if ( pErrLog && ( rc != NO_ERROR) ) { // // account lockout policy failed. // ScepBuildErrorLogInfo( ScepSceStatusToDosError(saveRc), pErrLog, SCEDLL_ERROR_QUERY_LOCKOUT ); } } // // Force Logoff when hour expire - (on non-DCs too) // Buffer = NULL; NtStatus = SamQueryInformationDomain( DomainHandle, DomainLogoffInformation, &Buffer ); rc = RtlNtStatusToDosError( NtStatus ); if ( NT_SUCCESS(NtStatus) ) { rc = SCESTATUS_SUCCESS; if ( ((DOMAIN_LOGOFF_INFORMATION *)Buffer)->ForceLogoff.HighPart == 0 && ((DOMAIN_LOGOFF_INFORMATION *)Buffer)->ForceLogoff.LowPart == 0 ) { // yes CurrentVal = 1; } else CurrentVal = 0; BaseVal = pTmpInfo->ForceLogoffWhenHourExpire; if ( dwSaveOption & SCEPOL_SYSTEM_SETTINGS ) { pSmpInfo->ForceLogoffWhenHourExpire = CurrentVal; } else if ( dwSaveOption ) { if ( ( CurrentVal != BaseVal ) && ( BaseVal != SCE_NO_VALUE) ) { pSmpInfo->ForceLogoffWhenHourExpire = CurrentVal; *pbChanged = TRUE; } else if ( dwSaveOption & SCEPOL_SAVE_DB ) { // // turn this item off to indicate this one is not changed // pSmpInfo->ForceLogoffWhenHourExpire = SCE_NO_VALUE; } } else { rc = ScepCompareAndSaveIntValue( hSection, L"ForceLogOffWhenHourExpire", (gOptions & SCE_GENERATE_ROLLBACK), (gOptions & SCE_NO_ANALYZE) ? SCE_SNAPSHOT_VALUE : BaseVal, CurrentVal); if ( rc == SCESTATUS_SUCCESS && !(gOptions & SCE_NO_ANALYZE) ) { AccessValues[IDX_FORCE_LOGOFF] = CurrentVal; } } SamFreeMemory(Buffer); if ( rc != SCESTATUS_SUCCESS ) { saveRc = rc; ScepLogOutput3(1, ScepSceStatusToDosError(rc), SCEDLL_SAP_ERROR_LOGOFF); if ( !(dwSaveOption & SCEPOL_SYSTEM_SETTINGS) ) { goto GETOUT; } } else ScepLogOutput3(1, 0, SCEDLL_SAP_LOGOFF ); } else { saveRc = ScepDosErrorToSceStatus(rc); ScepLogOutput3(1,rc, SCEDLL_ERROR_QUERY_LOGOFF); // ntstatus if ( !(dwSaveOption & SCEPOL_SYSTEM_SETTINGS) ) { goto GETOUT; } } if ( pErrLog && (rc != NO_ERROR) ) { // // force logoff failed. // ScepBuildErrorLogInfo( ScepSceStatusToDosError(saveRc), pErrLog, SCEDLL_ERROR_QUERY_LOGOFF ); } // // Check if Administrator/Guest accounts need to be renamed // if ( (dwSaveOption & SCEPOL_SYSTEM_SETTINGS) || (!(gOptions & SCE_NO_ANALYZE) && (dwSaveOption == 0)) ) { RegBuf=NULL; CurrentVal=0; if ( (gOptions & SCE_GENERATE_ROLLBACK) && (pSmpInfo->NewAdministratorName == NULL ) ) { NtStatus = STATUS_SUCCESS; ToRename = FALSE; } else { NtStatus = ScepAdminGuestAccountsToManage( DomainHandle, SCE_RENAME_ADMIN, pSmpInfo->NewAdministratorName, &ToRename, &RegBuf, &CurrentVal ); } rc = RtlNtStatusToDosError(NtStatus); if ( NT_SUCCESS( NtStatus ) ) { if ( dwSaveOption & SCEPOL_SYSTEM_SETTINGS ) { pSmpInfo->NewAdministratorName = RegBuf; } else { if ( ToRename ) { rc = ScepCompareAndSaveStringValue( hSection, L"NewAdministratorName", pSmpInfo->NewAdministratorName, RegBuf, CurrentVal*sizeof(WCHAR) ); rc = ScepSceStatusToDosError(rc); } ScepFree(RegBuf); } RegBuf=NULL; } else if ( !(dwSaveOption & SCEPOL_SYSTEM_SETTINGS) && !(gOptions & SCE_GENERATE_ROLLBACK) ) { // // raise this one // ScepRaiseErrorString( hSection, L"NewAdministratorName", NULL ); } if ( rc != NO_ERROR ) { saveRc = ScepDosErrorToSceStatus(rc); if ( !(dwSaveOption & SCEPOL_SYSTEM_SETTINGS) ) { ScepLogOutput3(1, rc, SCEDLL_SAP_ERROR_ADMINISTRATOR); goto GETOUT; } else if ( pErrLog ) { // // account name failed. // ScepBuildErrorLogInfo( rc, pErrLog, SCEDLL_SAP_ERROR_ADMINISTRATOR ); } } RegBuf=NULL; CurrentVal=0; if ( (gOptions & SCE_GENERATE_ROLLBACK) && (pSmpInfo->NewGuestName == NULL ) ) { NtStatus = STATUS_SUCCESS; ToRename = FALSE; } else { NtStatus = ScepAdminGuestAccountsToManage( DomainHandle, SCE_RENAME_GUEST, pSmpInfo->NewGuestName, &ToRename, &RegBuf, &CurrentVal ); } rc = RtlNtStatusToDosError(NtStatus); if ( NT_SUCCESS( NtStatus ) ) { if ( dwSaveOption & SCEPOL_SYSTEM_SETTINGS ) { pSmpInfo->NewGuestName = RegBuf; } else { if ( ToRename ) { rc = ScepCompareAndSaveStringValue( hSection, L"NewGuestName", pSmpInfo->NewGuestName, RegBuf, CurrentVal*sizeof(WCHAR) ); rc = ScepSceStatusToDosError(rc); } ScepFree(RegBuf); } RegBuf=NULL; } else if ( !(dwSaveOption & SCEPOL_SYSTEM_SETTINGS) && !(gOptions & SCE_GENERATE_ROLLBACK) ) { // // raise this one // ScepRaiseErrorString( hSection, L"NewGuestName", NULL ); } if ( rc != NO_ERROR ) { saveRc = ScepDosErrorToSceStatus(rc); if ( !(dwSaveOption & SCEPOL_SYSTEM_SETTINGS)) { ScepLogOutput3(1, rc, SCEDLL_SAP_ERROR_GUEST); goto GETOUT; } else if ( pErrLog ) { // // account name failed. // ScepBuildErrorLogInfo( rc, pErrLog, SCEDLL_SAP_ERROR_GUEST ); } } } // // Analyze LSA Anonymous Lookup information // rc = ScepGetLSAPolicyObjectInfo( &dwAllow ); if ( rc == ERROR_SUCCESS ) { CurrentVal = dwAllow; BaseVal = pTmpInfo->LSAAnonymousNameLookup; if ( dwSaveOption & SCEPOL_SYSTEM_SETTINGS ) { // // system setting // pSmpInfo->LSAAnonymousNameLookup = CurrentVal; } else if ( dwSaveOption ) { // // policy filter mode // // (this setting should not be filtered : bug #344311) /* if ( CurrentVal != BaseVal && BaseVal != SCE_NO_VALUE ) { pSmpInfo->LSAAnonymousNameLookup = CurrentVal; *pbChanged = TRUE; } else if ( dwSaveOption & SCEPOL_SAVE_DB ) { // // turn this item off to indicate this one is not changed // pSmpInfo->LSAAnonymousNameLookup = SCE_NO_VALUE; } */ } else { // // analyze // rc = ScepCompareAndSaveIntValue( hSection, L"LSAAnonymousNameLookup", (gOptions & SCE_GENERATE_ROLLBACK), (gOptions & SCE_NO_ANALYZE) ? SCE_SNAPSHOT_VALUE : BaseVal, CurrentVal); if ( rc == SCESTATUS_SUCCESS && !(gOptions & SCE_NO_ANALYZE) ) { AccessValues[IDX_LSA_ANON_LOOKUP] = CurrentVal; } } if ( rc != SCESTATUS_SUCCESS ) { saveRc = rc; ScepLogOutput3(1, ScepSceStatusToDosError(rc), SCEDLL_SAP_ERROR_LSA_ANON_LOOKUP); if ( !(dwSaveOption & SCEPOL_SYSTEM_SETTINGS) ) { goto GETOUT; } } else ScepLogOutput3(1, 0, SCEDLL_SAP_LSAPOLICY ); } else { saveRc = ScepDosErrorToSceStatus(rc); ScepLogOutput3(1,rc, SCEDLL_SAP_ERROR_LSA_ANON_LOOKUP); if ( !(dwSaveOption & SCEPOL_SYSTEM_SETTINGS) ) { goto GETOUT; } } if ( pErrLog && (rc != NO_ERROR) ) { // // lsa policy failed. // ScepBuildErrorLogInfo( ScepSceStatusToDosError(saveRc), pErrLog, SCEDLL_SAP_ERROR_LSA_ANON_LOOKUP ); } // // Admin/Guest accounts are not filtered (controlled by dwSaveOption flag) // if ( (dwSaveOption & SCEPOL_SYSTEM_SETTINGS) || (!(gOptions & SCE_NO_ANALYZE) && (dwSaveOption == 0)) ) { // // Analyze administrator account status // ToRename = FALSE; NtStatus = ScepAdminGuestAccountsToManage( DomainHandle, SCE_DISABLE_ADMIN, NULL, &ToRename, // TRUE = disabled NULL, NULL ); rc = RtlNtStatusToDosError(NtStatus); if ( NT_SUCCESS( NtStatus ) ) { if ( dwSaveOption & SCEPOL_SYSTEM_SETTINGS ) { pSmpInfo->EnableAdminAccount = ToRename ? 0 : 1; } else { rc = ScepCompareAndSaveIntValue( hSection, L"EnableAdminAccount", (gOptions & SCE_GENERATE_ROLLBACK), pSmpInfo->EnableAdminAccount, ToRename ? 0 : 1 ); if ( rc == SCESTATUS_SUCCESS && !(gOptions & SCE_NO_ANALYZE) ) { AccessValues[IDX_ENABLE_ADMIN] = ToRename ? 0 : 1; } rc = ScepSceStatusToDosError(rc); } } else if ( !(dwSaveOption & SCEPOL_SYSTEM_SETTINGS) && !(gOptions & SCE_GENERATE_ROLLBACK) ) { // // raise this one // ScepRaiseErrorString( hSection, L"EnableAdminAccount", NULL ); } if ( rc != NO_ERROR ) { saveRc = ScepDosErrorToSceStatus(rc); if ( !(dwSaveOption & SCEPOL_SYSTEM_SETTINGS)) { ScepLogOutput3(1, rc, SCEDLL_SAP_ERROR_DISABLE_ADMIN); goto GETOUT; } else if ( pErrLog ) { // // account name failed. // ScepBuildErrorLogInfo( rc, pErrLog, SCEDLL_SAP_ERROR_DISABLE_ADMIN ); } } // // Analyze administrator account status // ToRename = FALSE; NtStatus = ScepAdminGuestAccountsToManage( DomainHandle, SCE_DISABLE_GUEST, NULL, &ToRename, // TRUE=disabled NULL, NULL ); rc = RtlNtStatusToDosError(NtStatus); if ( NT_SUCCESS( NtStatus ) ) { if ( dwSaveOption & SCEPOL_SYSTEM_SETTINGS ) { pSmpInfo->EnableGuestAccount = ToRename ? 0 : 1; } else { rc = ScepCompareAndSaveIntValue( hSection, L"EnableGuestAccount", (gOptions & SCE_GENERATE_ROLLBACK), pSmpInfo->EnableGuestAccount, ToRename ? 0 : 1 ); if ( rc == SCESTATUS_SUCCESS && !(gOptions & SCE_NO_ANALYZE) ) { AccessValues[IDX_ENABLE_GUEST] = ToRename ? 0 : 1; } rc = ScepSceStatusToDosError(rc); } } else if ( !(dwSaveOption & SCEPOL_SYSTEM_SETTINGS) && !(gOptions & SCE_GENERATE_ROLLBACK) ) { // // raise this one // ScepRaiseErrorString( hSection, L"EnableGuestAccount", NULL ); } if ( rc != NO_ERROR ) { saveRc = ScepDosErrorToSceStatus(rc); if ( !(dwSaveOption & SCEPOL_SYSTEM_SETTINGS)) { ScepLogOutput3(1, rc, SCEDLL_SAP_ERROR_DISABLE_GUEST); goto GETOUT; } else if ( pErrLog ) { // // account name failed. // ScepBuildErrorLogInfo( rc, pErrLog, SCEDLL_SAP_ERROR_DISABLE_GUEST ); } } } // // compare the snapshot with SmpInfo and write to SAP section // ScepLogOutput3(1, 0, SCEDLL_SAP_OTHER_POLICY); GETOUT: // // Clear out memory and return // SamCloseHandle( DomainHandle ); SamCloseHandle( ServerHandle ); if ( DomainSid != NULL ) SamFreeMemory(DomainSid); // // see if there is anything needs to be raised for error // if ( (dwSaveOption == 0) && !(gOptions & SCE_NO_ANALYZE) && !(gOptions & SCE_GENERATE_ROLLBACK) ) { for ( CurrentVal=0; CurrentValUserAccountControl & USER_ACCOUNT_DISABLED ) { *ToRename = TRUE; } else { *ToRename = FALSE; } } else { // // check account names // Buffer = (USER_NAME_INFORMATION *)pInfoBuffer; if ( Buffer->UserName.Length > 0 && Buffer->UserName.Buffer ) { if (CurrentName) { *CurrentName = (PWSTR)ScepAlloc(0, Buffer->UserName.Length+2); if ( *CurrentName ) { wcsncpy(*CurrentName, Buffer->UserName.Buffer, Buffer->UserName.Length/2); (*CurrentName)[Buffer->UserName.Length/2] = L'\0'; } else NtStatus = STATUS_NO_MEMORY; } if ( pNameLen ) { *pNameLen = Buffer->UserName.Length/2; } if ( NT_SUCCESS( NtStatus ) && TargetName ) { if ( _wcsnicmp(Buffer->UserName.Buffer, TargetName, Buffer->UserName.Length/2 ) == 0 ) *ToRename = FALSE; } } } } SamFreeMemory(pInfoBuffer); SamCloseHandle( UserHandle1 ); } return( NtStatus ); } BOOL ScepIsThisItemInNameList( IN PWSTR Item, IN BOOL bIsSid, IN PSCE_NAME_LIST pList ) { PSCE_NAME_LIST pName2; BOOL bSid2; if ( Item == NULL || pList == NULL ) { return(FALSE); } for ( pName2=pList; pName2 != NULL; pName2 = pName2->Next ) { if ( pName2->Name == NULL ) { continue; } if ( ScepValidSid( (PSID)(pName2->Name) ) ) { bSid2 = TRUE; } else { bSid2 = FALSE; } // // if SID/Name format mismatch, return // if ( bIsSid != bSid2 ) { continue; } if ( bIsSid && RtlEqualSid( (PSID)(Item), (PSID)(pName2->Name) ) ) { // // find a SID match // break; } if ( !bIsSid && _wcsicmp(Item, pName2->Name) == 0 ) { // // find a match // break; // the second for loop } } if ( pName2 ) { // // find it // return(TRUE); } else { return(FALSE); } } BOOL ScepCompareSidNameList( IN PSCE_NAME_LIST pList1, IN PSCE_NAME_LIST pList2 ) /* Compare two lists where the name field can be a SID or Name. The rule is to compare SID to SID and name to name because name is only there when it can be mapped to a SID */ { PSCE_NAME_LIST pName1, pName2; DWORD CountSid1=0, CountSid2=0; DWORD CountN1=0, CountN2=0; if ( (pList2 == NULL && pList1 != NULL) || (pList2 != NULL && pList1 == NULL) ) { // return(TRUE); // should be not equal return(FALSE); } for ( pName2=pList2; pName2 != NULL; pName2 = pName2->Next ) { if ( pName2->Name == NULL ) { continue; } if ( ScepValidSid( (PSID)(pName2->Name) ) ) { CountSid2++; } else { CountN2++; } } BOOL bSid1; for ( pName1=pList1; pName1 != NULL; pName1 = pName1->Next ) { if ( pName1->Name == NULL ) { continue; } if ( ScepValidSid( (PSID)(pName1->Name) ) ) { bSid1 = TRUE; CountSid1++; } else { bSid1 = FALSE; CountN1++; } if ( !ScepIsThisItemInNameList( pName1->Name, bSid1, pList2 ) ) { // // does not find a match // return(FALSE); } } if ( CountSid1 != CountSid2 ) return(FALSE); if ( CountN2 != CountN2 ) { return(FALSE); } return(TRUE); } DWORD ScepConvertSidListToStringName( IN LSA_HANDLE LsaPolicy, IN OUT PSCE_NAME_LIST pList ) { PSCE_NAME_LIST pSidList; DWORD rc=ERROR_SUCCESS; PWSTR StringSid=NULL; for ( pSidList=pList; pSidList != NULL; pSidList=pSidList->Next) { if ( pSidList->Name == NULL ) { continue; } if ( ScepValidSid( (PSID)(pSidList->Name) ) ) { // // if the SID is a domain account, convert it to sid string // otherwise, convert it to name, then add to the name list // if ( ScepIsSidFromAccountDomain( (PSID)(pSidList->Name) ) ) { rc = ScepConvertSidToPrefixStringSid( (PSID)(pSidList->Name), &StringSid ); } else { // // should conver it to name // rc = RtlNtStatusToDosError( ScepConvertSidToName( LsaPolicy, (PSID)(pSidList->Name), FALSE, &StringSid, NULL )); } if ( rc == ERROR_SUCCESS ) { ScepFree( pSidList->Name ); pSidList->Name = StringSid; StringSid = NULL; } else { break; } } else { // // this is not a valid sid so it must be in name format already. // just leave it as it is. // } } return(rc); } SCESTATUS ScepAnalyzePrivileges( IN PSCE_PRIVILEGE_ASSIGNMENT pPrivilegeList ) /* ++ Routine Description: This routine analyzies local system privilege's direct assignments to accounts. Different assignment from the profile is saved to the SAP profile Arguments: pSmpInfo - The buffer which contains profile information to compare with Return value: SCESTATUS_SUCCESS SCESTATUS_NOT_ENOUGH_RESOURCE SCESTATUS_INVALID_PARAMETER -- */ { if ( (gOptions & SCE_GENERATE_ROLLBACK) && pPrivilegeList == NULL ) { return(SCESTATUS_SUCCESS); } NTSTATUS NtStatus = STATUS_SUCCESS; ULONG CountReturned; UNICODE_STRING UserRight; PLSA_ENUMERATION_INFORMATION EnumBuffer=NULL; LSA_HANDLE PolicyHandle=NULL; PLSA_TRANSLATED_NAME Names=NULL; PLSA_REFERENCED_DOMAIN_LIST ReferencedDomains=NULL; DWORD i=0, j; SCESTATUS rc; PSCE_NAME_LIST pNameList=NULL; PSCE_PRIVILEGE_ASSIGNMENT pPrivilege; DWORD nPrivCount=0; // // Prepare Jet's section to write // rc = ScepStartANewSection( hProfile, &hSection, (gOptions & SCE_NO_ANALYZE || gOptions & SCE_GENERATE_ROLLBACK) ? SCEJET_TABLE_SMP : SCEJET_TABLE_SAP, szPrivilegeRights ); if ( rc != SCESTATUS_SUCCESS ) { ScepLogOutput3(1, ScepSceStatusToDosError(rc), SCEDLL_SAP_START_SECTION, (PWSTR)szPrivilegeRights); goto Done; } // // Open LSA policy // NtStatus = ScepOpenLsaPolicy( GENERIC_READ | GENERIC_EXECUTE, &PolicyHandle, TRUE ); if ( !NT_SUCCESS(NtStatus) ) { rc = RtlNtStatusToDosError(NtStatus); ScepLogOutput3(1, rc, SCEDLL_LSA_POLICY ); ScepPostProgress(TICKS_PRIVILEGE, AREA_PRIVILEGES, NULL); goto Done; } // // enumerate accounts for each user right // INT iStat=0; for ( i=0; iNext ) { // should compare name, because value is different // if ( i == pPrivilege->Value) if ( _wcsicmp(SCE_Privileges[i].Name, pPrivilege->Name) == 0 ) break; } if ( pPrivilege == NULL ) { if ( gOptions & SCE_GENERATE_ROLLBACK ) continue; iStat = 2; } else { iStat = 1; } } RtlInitUnicodeString( &UserRight, (PCWSTR)(SCE_Privileges[i].Name)); ScepLogOutput3(1, 0, SCEDLL_SAP_ANALYZE, SCE_Privileges[i].Name); if ( nPrivCount < TICKS_PRIVILEGE ) { // // only post maximum TICKS_PRIVILEGE ticks because that's the number // remembers in the total ticks // ScepPostProgress(1, AREA_PRIVILEGES, SCE_Privileges[i].Name); nPrivCount++; } NtStatus = LsaEnumerateAccountsWithUserRight( PolicyHandle, &UserRight, (PVOID *)&EnumBuffer, // account SIDs &CountReturned ); if ( NT_SUCCESS(NtStatus) ) { BOOL bUsed; for ( j=0; jAssignedTo, pNameList) == FALSE ) { // // this priv does not exist in the SMP list, or have different // accounts assigned to. Save it // rc = ScepConvertSidListToStringName(PolicyHandle, pNameList); if ( SCESTATUS_SUCCESS == rc ) { rc = ScepWriteNameListValue( PolicyHandle, hSection, SCE_Privileges[i].Name, pNameList, SCE_WRITE_EMPTY_LIST, iStat ); if ( rc != SCESTATUS_SUCCESS ) { ScepLogOutput3(1, ScepSceStatusToDosError(rc), SCEDLL_SAP_ERROR_SAVE, SCE_Privileges[i].Name); NtStatus = STATUS_NO_MEMORY; goto Done; } } else if ( !(gOptions & SCE_NO_ANALYZE) && !(gOptions & SCE_GENERATE_ROLLBACK) ) { ScepRaiseErrorString( hSection, SCE_Privileges[i].Name, NULL ); } } if ( pNameList != NULL ) { ScepFreeNameList(pNameList); pNameList = NULL; } } } Done: if ( !(gOptions & SCE_NO_ANALYZE) && !(gOptions & SCE_GENERATE_ROLLBACK) ) { for ( j=i; jNext ) { if ( (gOptions & SCE_GENERATE_ROLLBACK) && (pGroup->Status & SCE_GROUP_STATUS_NC_MEMBERS) && (pGroup->Status & SCE_GROUP_STATUS_NC_MEMBEROF) ) { continue; } if ( (ProductType == NtProductLanManNt) && (pGroup->Status & SCE_GROUP_STATUS_DONE_IN_DS) ) { nGroupCount++; continue; } if ( KeyName ) { LocalFree(KeyName); KeyName = NULL; } pTemp = wcschr(pGroup->GroupName, L'\\'); if ( pTemp ) { // // there is a domain name, check it with computer/domain name // uName.Buffer = pGroup->GroupName; uName.Length = ((USHORT)(pTemp-pGroup->GroupName))*sizeof(TCHAR); if ( !ScepIsDomainLocal(&uName) ) { // If there is a memberof part, we'll configure it but // we don't support analysis. Gracefully continue. if(!pGroup->pMemberOf) { ScepLogOutput3(1, 0, SCEDLL_NO_MAPPINGS, pGroup->GroupName); rc = SCESTATUS_INVALID_DATA; } continue; } ScepConvertNameToSidString( PolicyHandle, pGroup->GroupName, FALSE, &KeyName, &GroupLen ); if(NULL == KeyName) // failed to map to SID, we'll use GroupName { GroupLen = wcslen(pGroup->GroupName); } pTemp++; } else { if ( ScepLookupWellKnownName( pGroup->GroupName, PolicyHandle, &KeyName ) ) { GroupLen = wcslen(KeyName); } else { KeyName = NULL; GroupLen = wcslen(pGroup->GroupName); } pTemp = pGroup->GroupName; } ScepLogOutput3(0,0, SCEDLL_SAP_ANALYZE, pGroup->GroupName); if ( nGroupCount < TICKS_GROUPS ) { ScepPostProgress(1, AREA_GROUP_MEMBERSHIP, pGroup->GroupName); nGroupCount++; } // initialize a UNICODE_STRING for the group name RtlInitUnicodeString(&Name, pTemp); // // lookup the group name in account domain first // NtStatus = SamLookupNamesInDomain( DomainHandle, 1, &Name, &GrpId, &GrpUse ); ThisDomain = DomainHandle; ThisDomainSid = DomainSid; if ( NtStatus == STATUS_NONE_MAPPED ) { // // not found in account domain. Lookup in the builtin domain // NtStatus = SamLookupNamesInDomain( BuiltinDomainHandle, 1, &Name, &GrpId, &GrpUse ); ThisDomain=BuiltinDomainHandle; ThisDomainSid = BuiltinDomainSid; } if ( NtStatus == STATUS_NONE_MAPPED ) { ScepLogOutput3(1, 0, SCEDLL_NO_MAPPINGS, pGroup->GroupName); gWarningCode = ERROR_SOME_NOT_MAPPED; NtStatus = STATUS_SUCCESS; if ( !(gOptions & SCE_GENERATE_ROLLBACK) ) { ScepRaiseErrorString( hSection, KeyName ? KeyName : pGroup->GroupName, szMembers ); } continue; } else if ( !NT_SUCCESS(NtStatus) ) { Win32rc = RtlNtStatusToDosError(NtStatus); ScepLogOutput3(1, Win32rc, SCEDLL_NO_MAPPINGS, pGroup->GroupName); rc = ScepDosErrorToSceStatus(Win32rc); goto Done; } if ( GrpId[0] == DOMAIN_GROUP_RID_USERS ) { // || // GrpId[0] == DOMAIN_ALIAS_RID_USERS ) // // do not check this one // if ( !(gOptions & SCE_GENERATE_ROLLBACK) ) { ScepRaiseErrorString( hSection, KeyName ? KeyName : pGroup->GroupName, szMembers ); } SamFreeMemory(GrpId); GrpId = NULL; SamFreeMemory(GrpUse); GrpUse = NULL; continue; } NtStatus = ScepDomainIdToSid( ThisDomainSid, GrpId[0], &GrpSid ); if ( !NT_SUCCESS(NtStatus) ) { Win32rc = RtlNtStatusToDosError(NtStatus); rc = ScepDosErrorToSceStatus(Win32rc); goto Done; } // open the group to get a handle switch ( GrpUse[0] ) { case SidTypeGroup: NtStatus = SamOpenGroup( ThisDomain, GROUP_READ | GROUP_EXECUTE, GrpId[0], &GroupHandle ); break; case SidTypeAlias: NtStatus = SamOpenAlias( ThisDomain, ALIAS_READ | ALIAS_EXECUTE, GrpId[0], &GroupHandle ); break; default: NtStatus = STATUS_DATA_ERROR; ScepLogOutput3(1, 0, SCEDLL_INVALID_GROUP, pGroup->GroupName); } if ( !NT_SUCCESS(NtStatus) ) { Win32rc = RtlNtStatusToDosError(NtStatus); ScepLogOutput3(1, Win32rc, SCEDLL_ERROR_OPEN, pGroup->GroupName); rc = ScepDosErrorToSceStatus(Win32rc); goto Done; } // // // compare members for the group // NtStatus = ScepCompareMembersOfGroup( ThisDomain, ThisDomainSid, PolicyHandle, GrpUse[0], GroupHandle, pGroup->pMembers, &pGroupMembers, &bDifferent ); if ( !NT_SUCCESS(NtStatus) ) { Win32rc = RtlNtStatusToDosError(NtStatus); ScepLogOutput3(1, Win32rc, SCEDLL_ERROR_ANALYZE_MEMBERS, pGroup->GroupName); rc = ScepDosErrorToSceStatus(Win32rc); if ( STATUS_NONE_MAPPED == NtStatus ) { SamFreeMemory(GrpId); GrpId = NULL; SamFreeMemory(GrpUse); GrpUse = NULL; ScepFree(GrpSid); GrpSid = NULL; SamCloseHandle(GroupHandle); GroupHandle = NULL; continue; } else { goto Done; } } // // save members for the group // if there is any member difference, then save the whole member list // if ( bDifferent || ( (gOptions & SCE_GENERATE_ROLLBACK) && (pGroup->Status & SCE_GROUP_STATUS_NC_MEMBERS) ) ) { // // not same members, or not configured. Save pGroupMembers now // rc = ScepSaveMemberMembershipList( PolicyHandle, szMembers, KeyName ? KeyName : pGroup->GroupName, GroupLen, pGroupMembers, ( pGroup->Status & SCE_GROUP_STATUS_NC_MEMBERS ) ? 2 : 1 ); } ScepFreeNameList(pGroupMembers); pGroupMembers = NULL; // // get memberof list // NtStatus = ScepGetGroupsForAccount( DomainHandle, BuiltinDomainHandle, GroupHandle, GrpSid, &pGroupsMemberof ); if ( !NT_SUCCESS(NtStatus) ) { Win32rc = RtlNtStatusToDosError(NtStatus); ScepLogOutput3(1, Win32rc, SCEDLL_ERROR_ANALYZE_MEMBEROF, pGroup->GroupName); rc = ScepDosErrorToSceStatus(Win32rc); goto Done; } // // not configured, or compare and save MemberOf for the group // pMemberOf must not have domain prefix because they must be alias // if ( ( pGroup->Status & SCE_GROUP_STATUS_NC_MEMBEROF ) || SceCompareNameList(pGroup->pMemberOf, pGroupsMemberof) == FALSE ) { // // there is difference. Save the memberOf // INT iStat = 0; if ( (pGroup->Status & SCE_GROUP_STATUS_NC_MEMBEROF) || (pGroup->pMemberOf == NULL) ) { iStat = 2; } else { iStat = 1; } rc = ScepSaveMemberMembershipList( PolicyHandle, szMemberof, KeyName ? KeyName : pGroup->GroupName, GroupLen, pGroupsMemberof, iStat ); } ScepFreeNameList(pGroupsMemberof); pGroupsMemberof = NULL; SamFreeMemory(GrpId); GrpId = NULL; SamFreeMemory(GrpUse); GrpUse = NULL; ScepFree(GrpSid); GrpSid = NULL; SamCloseHandle(GroupHandle); GroupHandle = NULL; } Done: if ( KeyName != NULL ) ScepFree(KeyName); if ( GrpId != NULL ) SamFreeMemory(GrpId); if ( GrpUse != NULL ) SamFreeMemory(GrpUse); if ( GrpSid != NULL ) ScepFree(GrpSid); if ( pGroupMembers != NULL ) ScepFreeNameList( pGroupMembers ); if ( pGroupsMemberof != NULL ) ScepFreeNameList( pGroupsMemberof ); if ( pPrivilegesHeld != NULL ) ScepFreeNameStatusList( pPrivilegesHeld ); SamCloseHandle(GroupHandle); // // raise groups that are errored // if ( !(gOptions & SCE_GENERATE_ROLLBACK) ) { for ( PSCE_GROUP_MEMBERSHIP pTmpGrp=pGroup; pTmpGrp != NULL; pTmpGrp = pTmpGrp->Next ) { if ( pTmpGrp->GroupName == NULL ) { continue; } if ( pTmpGrp->Status & SCE_GROUP_STATUS_DONE_IN_DS ) { continue; } if ( wcschr(pTmpGrp->GroupName, L'\\') ) { ScepConvertNameToSidString( PolicyHandle, pTmpGrp->GroupName, FALSE, &KeyName, &GroupLen ); } else { if ( !ScepLookupWellKnownName( pTmpGrp->GroupName, PolicyHandle, &KeyName ) ) { KeyName = NULL; } } ScepRaiseErrorString( hSection, KeyName ? KeyName : pTmpGrp->GroupName, szMembers ); if ( KeyName ) { LocalFree(KeyName); KeyName = NULL; } } } LsaClose( PolicyHandle); SamCloseHandle( DomainHandle ); SamCloseHandle( BuiltinDomainHandle ); SamCloseHandle( ServerHandle ); if ( DomainSid != NULL ) { SamFreeMemory(DomainSid); } if ( BuiltinDomainSid != NULL ) { RtlFreeSid(BuiltinDomainSid); } // // post progress to the end of this area // if ( nGroupCount < TICKS_GROUPS ) { ScepPostProgress(TICKS_GROUPS-nGroupCount, AREA_GROUP_MEMBERSHIP, NULL); } return(rc); } SCESTATUS ScepSaveMemberMembershipList( IN LSA_HANDLE LsaPolicy, IN PCWSTR szSuffix, IN PWSTR GroupName, IN DWORD GroupLen, IN PSCE_NAME_LIST pList, IN INT Status ) { PWSTR KeyName; SCESTATUS rc; if ( szSuffix == NULL || GroupName == NULL || GroupLen == 0 ) { return(SCESTATUS_INVALID_PARAMETER); } KeyName = (PWSTR)ScepAlloc(0, (GroupLen+wcslen(szSuffix)+1)*sizeof(WCHAR)); if ( KeyName != NULL ) { swprintf(KeyName, L"%s%s", GroupName, szSuffix); rc = ScepWriteNameListValue( LsaPolicy, hSection, KeyName, pList, SCE_WRITE_EMPTY_LIST | SCE_WRITE_CONVERT, Status ); if ( rc != SCESTATUS_SUCCESS ) { ScepLogOutput3(1, ScepSceStatusToDosError(rc), SCEDLL_SAP_ERROR_SAVE, GroupName); } ScepFree(KeyName); } else rc = SCESTATUS_NOT_ENOUGH_RESOURCE; return(rc); } NTSTATUS ScepCompareMembersOfGroup( IN SAM_HANDLE DomainHandle, IN PSID ThisDomainSid, IN LSA_HANDLE PolicyHandle, IN SID_NAME_USE GrpUse, IN SAM_HANDLE GroupHandle, IN PSCE_NAME_LIST pChkMembers, OUT PSCE_NAME_LIST *ppMembers, OUT PBOOL bDifferent ) /* ++ Routine Description: This routine compares members in a group GroupHandle in the domain specified by ThisDomainSid with the pChkMembers list. If there is mismatch, the current members are added to the output list ppMembers. The domain can be account domain or the builtin domain. Groups can be global groups or aliases, indicated by GrpUse. Arguments: ThisDomainSid - The domain SID PolicyHandle - The LSA policy handle GrpUse - The "type" of the group GroupHandle - the group handle pChkMembers - The member list to check with ppMembers - The group members list to output Return value: NTSTATUS -- */ { NTSTATUS NtStatus=ERROR_SUCCESS; PULONG MemberIds=NULL; PULONG Attributes=NULL; ULONG MemberCount=0; PUNICODE_STRING Names=NULL; PSID_NAME_USE Use=NULL; PSID *MemberAliasSids=NULL; PLSA_REFERENCED_DOMAIN_LIST ReferencedDomains=NULL; PLSA_TRANSLATED_NAME LsaNames=NULL; PUNICODE_STRING MemberNames=NULL; PSID *Sids=NULL; ULONG ChkCount=0; DWORD i, j; DWORD rc; BOOL bMismatch; *ppMembers = NULL; *bDifferent = FALSE; switch ( GrpUse ) { case SidTypeGroup: NtStatus = SamGetMembersInGroup( GroupHandle, &MemberIds, &Attributes, &MemberCount ); if ( !NT_SUCCESS(NtStatus) ) goto Done; // // group members only exist in the same domain as the group // if ( MemberCount > 0 ) { NtStatus = SamLookupIdsInDomain( DomainHandle, MemberCount, MemberIds, &Names, &Use ); } break; case SidTypeAlias: // // members of alias may exist in everywhere // NtStatus = SamGetMembersInAlias( GroupHandle, &MemberAliasSids, &MemberCount ); break; default: NtStatus = STATUS_DATA_ERROR; ScepLogOutput3(1, 0, SCEDLL_INVALID_GROUP); return(NtStatus); } if ( !NT_SUCCESS(NtStatus) ) goto Done; if ( GrpUse == SidTypeGroup ) { // // add members to the list // DomainHandle must point to a account domain because builtin domain // won't have SidTypeGroup account // PDOMAIN_NAME_INFORMATION DomainName=NULL; SamQueryInformationDomain( DomainHandle, DomainNameInformation, (PVOID *)&DomainName ); for (j=0; jDomainName.Length > 0 && DomainName->DomainName.Buffer ) { rc = ScepAddTwoNamesToNameList( ppMembers, TRUE, DomainName->DomainName.Buffer, DomainName->DomainName.Length/2, Names[j].Buffer, Names[j].Length/2 ); } else { rc = ScepAddToNameList(ppMembers, Names[j].Buffer, Names[j].Length/2); } #ifdef SCE_DBG wprintf(L"rc=%d, Add %s to Members list\n", rc, Names[j].Buffer); #endif } // compare with pChkMembers if ( !(gOptions & SCE_NO_ANALYZE) && ScepCompareGroupNameList(&(DomainName->DomainName), pChkMembers, *ppMembers) == TRUE ) { // // it is same. return NULL for ppMembers // ScepFreeNameList(*ppMembers); *ppMembers = NULL; } else { *bDifferent = TRUE; } if ( DomainName ) { SamFreeMemory(DomainName); DomainName = NULL; } } else { // alias // translate pChkMembers to Sids NtStatus = ScepGetMemberListSids( ThisDomainSid, PolicyHandle, pChkMembers, &MemberNames, &Sids, &ChkCount ); bMismatch = FALSE; // // if returned error, we consider the members different // if ( NT_SUCCESS(NtStatus) && !(gOptions & SCE_NO_ANALYZE) ) { /* // The number of members on system and in the configuration must match, including // unmapped accounts. DWORD newCount=0; for ( i=0; i= MemberCount ) // not find a match break; } else { // a mismatch for unmapped account break; } } if ( i < ChkCount ) // something mismatch bMismatch = TRUE; } else bMismatch = TRUE; } else if ( NtStatus != STATUS_NO_MEMORY ) { NtStatus = STATUS_SUCCESS; bMismatch = TRUE; } *bDifferent = bMismatch; if ( bMismatch ) { // // translate SID into names // if ( MemberCount > 0 ) { NtStatus = LsaLookupSids( PolicyHandle, MemberCount, MemberAliasSids, &ReferencedDomains, &LsaNames ); if ( !NT_SUCCESS(NtStatus) ) goto Done; } // // add members to the list to output // PWSTR StringSid; for (j=0; jEntries > 0 && LsaNames[0].Use != SidTypeWellKnownGroup && ReferencedDomains->Domains != NULL && LsaNames[j].DomainIndex != -1 && (ULONG)(LsaNames[j].DomainIndex) < ReferencedDomains->Entries && ScepIsSidFromAccountDomain( ReferencedDomains->Domains[LsaNames[j].DomainIndex].Sid ) ) { // // should add both the domain name and the account name // rc = ScepAddTwoNamesToNameList( ppMembers, TRUE, ReferencedDomains->Domains[LsaNames[j].DomainIndex].Name.Buffer, ReferencedDomains->Domains[LsaNames[j].DomainIndex].Name.Length/2, LsaNames[j].Name.Buffer, LsaNames[j].Name.Length/2 ); #ifdef SCE_DBG wprintf(L"rc=%d, Add %s\\%s to Members list\n", rc, ReferencedDomains->Domains[LsaNames[j].DomainIndex].Name.Buffer, LsaNames[j].Name.Buffer); #endif } else { rc = ScepAddToNameList(ppMembers, LsaNames[j].Name.Buffer, LsaNames[j].Name.Length/2); #ifdef SCE_DBG wprintf(L"rc=%d, Add %s to Members list\n", rc, LsaNames[j].Name.Buffer); #endif } if ( rc != NO_ERROR) { NtStatus = STATUS_NO_MEMORY; goto Done; } } } } Done: if (Use != NULL) SamFreeMemory(Use); if (Names != NULL) SamFreeMemory(Names); if (MemberIds != NULL) SamFreeMemory(MemberIds); if (Attributes != NULL) SamFreeMemory(Attributes); if (MemberAliasSids != NULL) SamFreeMemory(MemberAliasSids); if (ReferencedDomains != NULL) LsaFreeMemory(ReferencedDomains); if (LsaNames != NULL) LsaFreeMemory(LsaNames); if ( Sids != NULL ) { for ( i=0; i 0 ) { pTemp = Drives; while ( *pTemp != L'\0') { dType = GetDriveType(pTemp); Len = wcslen(pTemp); if ( dType == DRIVE_FIXED || dType == DRIVE_RAMDISK ) { // // add this to the root object list (check duplicate) // pTemp[Len-1] = L'\0'; // take out the '\' rc = ScepAddToObjectList( pRoots, pTemp, Len-1, // only drive letter and ':' is added TRUE, SCE_STATUS_IGNORE, 0, SCE_CHECK_DUP // TRUE ); if ( rc != ERROR_SUCCESS ) { // // Log the error and continue // ScepLogOutput3(1, rc, SCEDLL_SAP_ERROR_ADD, pTemp); if ( rc == ERROR_NOT_ENOUGH_MEMORY ) { rc = SCESTATUS_NOT_ENOUGH_RESOURCE; break; } else { // ignore other errors rc = SCESTATUS_SUCCESS; } } } // // go to next drive // pTemp += (Len + 1); } } else { // // ignore this error, only log it // ScepLogOutput3(1, GetLastError(), SCEDLL_ERROR_QUERY_INFO, L"file system"); } return(rc); } SCESTATUS ScepAnalyzeObjectSecurity( IN PSCE_OBJECT_LIST pRoots, IN AREA_INFORMATION Area, IN BOOL bSystemDb ) /* ++ Routine Description: Analyze the security setting on objects (registry keys, files, etc) as specified in pObjectCheckList. The Recursive is TRUE, all sub keys/ directories under a object will be checked too. All checked objects and their security settings are returned in ppObjectChecked Arguments: pObjectCheckList - a n-tree of objects to check ObjectType - Indicate permission or auditing Recursive - TRUE = check all subkeys/directories Return value: SCESTATUS error codes ++ */ { SCESTATUS rc; DWORD Win32rc=NO_ERROR; HANDLE Token; PSCE_OBJECT_LIST pOneRoot; PSCE_OBJECT_CHILD_LIST pSecurityObject=NULL; PCWSTR SectionName=NULL; DWORD FileSystemFlags; PSCE_OBJECT_LIST pTempRoot=NULL; WCHAR theDrive[4]; UINT DriveType; if ( pRoots == NULL && Area != AREA_DS_OBJECTS ) { return(SCESTATUS_SUCCESS); } switch (Area) { case AREA_REGISTRY_SECURITY: SectionName = szRegistryKeys; break; case AREA_FILE_SECURITY: SectionName = szFileSecurity; break; #if 0 case AREA_DS_OBJECTS: SectionName = szDSSecurity; break; #endif default: return(SCESTATUS_INVALID_PARAMETER); } // // get current thread/process's token // if (!OpenThreadToken( GetCurrentThread(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, //TOKEN_ALL_ACCESS, TRUE, &Token)) { if(ERROR_NO_TOKEN == GetLastError()){ if(!OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &Token)){ ScepLogOutput3(1, GetLastError(), SCEDLL_ERROR_QUERY_INFO, L"TOKEN"); return(ScepDosErrorToSceStatus(GetLastError())); } } else { ScepLogOutput3(1, GetLastError(), SCEDLL_ERROR_QUERY_INFO, L"TOKEN"); return(ScepDosErrorToSceStatus(GetLastError())); } } // // SE_SECURITY_PRIVILEGE, ignore the error if can't adjust privilege // Win32rc = SceAdjustPrivilege( SE_SECURITY_PRIVILEGE, TRUE, Token ); if ( Win32rc != NO_ERROR ) { ScepLogOutput3(1, Win32rc, SCEDLL_ERROR_ADJUST, L"SE_SECURITY_PRIVILEGE"); } // // Prepare JET section to write to // rc = ScepStartANewSection( hProfile, &hSection, SCEJET_TABLE_SAP, SectionName ); if ( rc != SCESTATUS_SUCCESS ) { ScepLogOutput3(1, ScepSceStatusToDosError(rc), SCEDLL_SAP_START_SECTION, (PWSTR)SectionName); goto Done; } #if 0 if ( Area == AREA_DS_OBJECTS && pRoots == NULL ) { // // nothing specified in the template, then save the domain in SAP // rc = ScepLdapOpen(NULL); if ( rc == SCESTATUS_SUCCESS ) { rc = ScepEnumerateDsObjectRoots(NULL, &pTempRoot); if ( rc == SCESTATUS_SUCCESS ) { rc = ScepSaveDsStatusToSection( pTempRoot->Name, TRUE, SCE_STATUS_NOT_CONFIGURED, NULL, 0 ); ScepFreeObjectList(pTempRoot); } ScepLdapClose(NULL); } if ( rc != SCESTATUS_SUCCESS ) { ScepLogOutput3(1, ScepSceStatusToDosError(rc), SCEDLL_SAP_ERROR_SAVE, SectionName); } } #endif // // Process each root // note: SCE_STATUS_NO_AUTO_INHERIT is treated the same as SCE_STATUS_CHECK // in analysis // for ( pOneRoot=pRoots; pOneRoot != NULL; pOneRoot=pOneRoot->Next ) { if ( Area == AREA_FILE_SECURITY && (pOneRoot->Status == SCE_STATUS_CHECK || pOneRoot->Status == SCE_STATUS_NO_AUTO_INHERIT || pOneRoot->Status == SCE_STATUS_OVERWRITE) ) { // // make sure the input data follows file format // if ( pOneRoot->Name[1] != L'\0' && pOneRoot->Name[1] != L':') { ScepLogOutput3(1, ERROR_INVALID_DATA, SCEDLL_CANNOT_FIND, pOneRoot->Name); rc = ScepSaveObjectString( hSection, pOneRoot->Name, FALSE, SCE_STATUS_ERROR_NOT_AVAILABLE, NULL, 0 ); continue; } // // check if support acl // theDrive[0] = pOneRoot->Name[0]; theDrive[1] = L':'; theDrive[2] = L'\\'; theDrive[3] = L'\0'; DriveType = GetDriveType(theDrive); if ( DriveType == DRIVE_FIXED || DriveType == DRIVE_RAMDISK ) { if ( GetVolumeInformation(theDrive, NULL, 0, NULL, NULL, &FileSystemFlags, NULL, 0 ) ) { if ( !( FileSystemFlags & FS_PERSISTENT_ACLS) ) { pOneRoot->Status = SCE_STATUS_NO_ACL_SUPPORT; rc = ScepSaveObjectString( hSection, theDrive, TRUE, SCE_STATUS_NO_ACL_SUPPORT, NULL, 0 ); if ( rc != SCESTATUS_SUCCESS ) goto Done; continue; } } else { // // ignore the error and treat the drive as NTFS // if it is not, it will error out later // ScepLogOutput3(1, GetLastError(), SCEDLL_ERROR_QUERY_VOLUME, theDrive); } } else { rc = ScepSaveObjectString( hSection, theDrive, TRUE, SCE_STATUS_NO_ACL_SUPPORT, NULL, 0 ); if ( rc != SCESTATUS_SUCCESS ) goto Done; continue; } } if ( pOneRoot->Status != SCE_STATUS_CHECK && pOneRoot->Status != SCE_STATUS_NO_AUTO_INHERIT && pOneRoot->Status != SCE_STATUS_OVERWRITE ) { // // log a point in SAP for not analyzing // if ( Area == AREA_DS_OBJECTS ) { rc = ScepSaveDsStatusToSection( pOneRoot->Name, TRUE, SCE_STATUS_NOT_CONFIGURED, NULL, 0 ); } else { rc = ScepSaveObjectString( hSection, pOneRoot->Name, TRUE, SCE_STATUS_NOT_CONFIGURED, NULL, 0 ); } if ( rc != SCESTATUS_SUCCESS ) goto Done; continue; } // // read scp information for this area // rc = ScepGetOneSection( hProfile, Area, pOneRoot->Name, bSystemDb ? SCE_ENGINE_SCP : SCE_ENGINE_SMP, (PVOID *)&pSecurityObject ); if ( rc != SCESTATUS_SUCCESS ) goto Done; if ( pSecurityObject == NULL ) { continue; } // // then process each node in the list // for (PSCE_OBJECT_CHILD_LIST pTemp = pSecurityObject; pTemp != NULL; pTemp=pTemp->Next) { if ( pTemp->Node == NULL ) continue; if ( Area == AREA_FILE_SECURITY ) { if ( pTemp->Node->ObjectFullName[1] == L':' && pTemp->Node->ObjectFullName[2] == L'\0' ) { pTemp->Node->ObjectFullName[2] = L'\\'; pTemp->Node->ObjectFullName[3] = L'\0'; } } // // compute the "real" security descriptor for each node, // Ds objects DO NOT need to be computed // if ( Area == AREA_FILE_SECURITY ) { rc = ScepCalculateSecurityToApply( pTemp->Node, SE_FILE_OBJECT, Token, &FileGenericMapping ); } else if ( Area == AREA_REGISTRY_SECURITY ) { rc = ScepCalculateSecurityToApply( pTemp->Node, SE_REGISTRY_KEY, Token, &KeyGenericMapping ); } if ( rc != SCESTATUS_SUCCESS ) { ScepLogOutput3(1, ScepSceStatusToDosError(rc), SCEDLL_ERROR_COMPUTESD, pTemp->Node->ObjectFullName); goto Done; } else { BadCnt = 0; if ( Area == AREA_FILE_SECURITY ) { // // analyze file object tree // Win32rc = ScepAnalyzeOneObjectInTree( pTemp->Node, SE_FILE_OBJECT, Token, &FileGenericMapping ); } else if ( Area == AREA_REGISTRY_SECURITY ) { // // analyze registry object tree // Win32rc = ScepAnalyzeOneObjectInTree( pTemp->Node, SE_REGISTRY_KEY, Token, &KeyGenericMapping ); } else { // // analyze ds objects // Win32rc = ScepAnalyzeDsSecurity( pTemp->Node ); } ScepLogOutput3(0, Win32rc, IDS_ANALYSIS_MISMATCH, BadCnt, pTemp->Node->ObjectFullName); rc = ScepDosErrorToSceStatus(Win32rc); } if ( rc != ERROR_SUCCESS ) { break; } } ScepFreeObject2Security( pSecurityObject, FALSE); pSecurityObject = NULL; } Done: // // if privilege is adjusted, turn it off. // SceAdjustPrivilege( SE_SECURITY_PRIVILEGE, FALSE, Token ); CloseHandle(Token); if ( pSecurityObject != NULL ) ScepFreeObject2Security( pSecurityObject, FALSE); return(rc); } DWORD ScepAnalyzeOneObjectInTree( IN PSCE_OBJECT_TREE ThisNode, IN SE_OBJECT_TYPE ObjectType, IN HANDLE Token, IN PGENERIC_MAPPING GenericMapping ) /* ++ Routine Description: Recursively Analyze each node in the object tree. If Recursive is set to TRUE, all sub keys/directories under an node will be analyzed too. All files/directories with DIFFERENT security setting from the profile are returned in ppObjectChecked. Arguments: ThisNode - one node in the n-tree to analyze ObjectType - Indicate file object or registry object Token - thread token used to compute creator owner GenericMapping - generic access mapping Return value: SCESTATUS error codes ++ */ { DWORD rc=NO_ERROR; BOOL StartChecking = FALSE; PSCE_OBJECT_TREE pTempNode; if ( ThisNode == NULL ) { return(SCESTATUS_SUCCESS); } if ( ThisNode->Status != SCE_STATUS_CHECK && ThisNode->Status != SCE_STATUS_NO_AUTO_INHERIT && ThisNode->Status != SCE_STATUS_OVERWRITE ) { // // Log a point in SAP // rc = ScepSaveObjectString( hSection, ThisNode->ObjectFullName, ThisNode->IsContainer, SCE_STATUS_NOT_CONFIGURED, NULL, 0 ); goto SkipNode; } if ( ThisNode->pSecurityDescriptor != NULL ) { // // notify the progress bar if there is any // switch(ObjectType) { case SE_FILE_OBJECT: ScepPostProgress(1, AREA_FILE_SECURITY, ThisNode->ObjectFullName); break; case SE_REGISTRY_KEY: ScepPostProgress(1, AREA_REGISTRY_SECURITY, ThisNode->ObjectFullName); break; default: ScepPostProgress(1, 0, ThisNode->ObjectFullName); break; } } // // find if this is the first node in this path to be configured // for ( pTempNode=ThisNode; pTempNode != NULL; pTempNode = pTempNode->Parent ) { if ( NULL != pTempNode->pApplySecurityDescriptor ) { StartChecking = TRUE; break; } } if ( StartChecking && ( NULL != ThisNode->pSecurityDescriptor) || ( ThisNode->Status == SCE_STATUS_OVERWRITE ) ) { // // only analyze objects with explicit aces specified, // or when parent's status is overwrite // if this node doesn't have a SD, it's status is coming from the parent // // process this node first // rc = ScepAnalyzeObjectOnly( ThisNode->ObjectFullName, ThisNode->IsContainer, ObjectType, ThisNode->pApplySecurityDescriptor, ThisNode->SeInfo ); // // if the object denies access, skip it. // if ( rc == ERROR_ACCESS_DENIED || rc == ERROR_CANT_ACCESS_FILE || rc == ERROR_SHARING_VIOLATION) { // // log a point in SAP for skipping // gWarningCode = rc; goto ProcChild; } // // if the object specified in the profile does not exist, skip it and children // if ( rc == ERROR_FILE_NOT_FOUND || rc == ERROR_PATH_NOT_FOUND || rc == ERROR_INVALID_HANDLE ) { gWarningCode = rc; rc = SCESTATUS_SUCCESS; goto SkipNode; } } else { // // log a point in SAP for not analyzing // rc = ScepSaveObjectString( hSection, ThisNode->ObjectFullName, ThisNode->IsContainer, SCE_STATUS_CHILDREN_CONFIGURED, NULL, 0 ); } if ( rc != ERROR_SUCCESS ) return(rc); // // only process the child objects if the flag is overwrite // because for auto-inherit or no inherit case, all child objects will // be enumerated as "N.C." status by GetObjectChildren api. // PSCE_OBJECT_CHILD_LIST pTemp; if ( (StartChecking && ThisNode->Status == SCE_STATUS_OVERWRITE) ) { //|| // (!StartChecking && NULL != ThisNode->LeftChild ) ) { // // analyze security for other files/keys under this directory // or log SAP point for not analyzing // DWORD BufSize; PWSTR Buffer=NULL; INT i; DWORD EnumRc=0; switch ( ObjectType ) { case SE_FILE_OBJECT: struct _wfinddata_t * pFileInfo; // // find all files under this directory/file // pFileInfo = (struct _wfinddata_t *)ScepAlloc(0,sizeof(struct _wfinddata_t)); if ( pFileInfo == NULL ) { rc = ERROR_NOT_ENOUGH_MEMORY; break; } BufSize = wcslen(ThisNode->ObjectFullName)+4; Buffer = (PWSTR)ScepAlloc( 0, (BufSize+1)*sizeof(WCHAR)); if ( Buffer == NULL ) { ScepFree(pFileInfo); pFileInfo = NULL; rc = ERROR_NOT_ENOUGH_MEMORY; break; } BOOL BackSlashExist; intptr_t hFile; BackSlashExist = ScepLastBackSlash(ThisNode->ObjectFullName); if ( BackSlashExist ) swprintf(Buffer, L"%s*.*", ThisNode->ObjectFullName); else swprintf(Buffer, L"%s\\*.*", ThisNode->ObjectFullName); hFile = _wfindfirst(Buffer, pFileInfo); ScepFree(Buffer); Buffer = NULL; if ( hFile != -1 && ( 0 == ( GetFileAttributes(ThisNode->ObjectFullName) & // bug 635098: don't propagate permissions FILE_ATTRIBUTE_REPARSE_POINT ))) // across junction points { do { if ( wcscmp(L"..", pFileInfo->name) == 0 || wcscmp(L".", pFileInfo->name) == 0 ) continue; // // if the file/subdir is in the children list // process it later. // for ( pTemp = ThisNode->ChildList, i=-1; pTemp != NULL; pTemp = pTemp->Next ) { if ( pTemp->Node == NULL ) continue; i=_wcsicmp(pTemp->Node->Name, pFileInfo->name); if ( i == 0 ) break; } if ( pTemp == NULL || i != 0 ) { // // The name is not in the list, so analyze this one // BufSize = wcslen(ThisNode->ObjectFullName)+wcslen(pFileInfo->name)+1; Buffer = (PWSTR)ScepAlloc( 0, (BufSize+1)*sizeof(WCHAR)); if ( Buffer == NULL ) { rc = ERROR_NOT_ENOUGH_MEMORY; break; } if ( BackSlashExist ) swprintf(Buffer, L"%s%s", ThisNode->ObjectFullName, pFileInfo->name); else swprintf(Buffer, L"%s\\%s", ThisNode->ObjectFullName, pFileInfo->name); EnumRc = pFileInfo->attrib; // borrow this variable temperaorily ScepFree(pFileInfo); pFileInfo = NULL; // if ( StartChecking ) { // raise N.C. status even for SCE_STATUS_CHECK if ( StartChecking && ThisNode->Status == SCE_STATUS_OVERWRITE ) { // // do not check owner and group information // if ( EnumRc & _A_SUBDIR ) { rc = ScepAnalyzeObjectAndChildren( Buffer, ObjectType, NULL, (ThisNode->SeInfo & DACL_SECURITY_INFORMATION) | (ThisNode->SeInfo & SACL_SECURITY_INFORMATION) ); } else { rc = ScepAnalyzeObjectOnly( Buffer, FALSE, ObjectType, NULL, (ThisNode->SeInfo & DACL_SECURITY_INFORMATION) | (ThisNode->SeInfo & SACL_SECURITY_INFORMATION) ); } } else { /* // // Log the SAP point // rc = ScepSaveObjectString( hSection, Buffer, (EnumRc & _A_SUBDIR) ? TRUE : FALSE, SCE_STATUS_NOT_CONFIGURED, NULL, 0 ); */ } ScepFree(Buffer); Buffer = NULL; if ( rc != ERROR_SUCCESS ) break; pFileInfo = (struct _wfinddata_t *)ScepAlloc(0,sizeof(struct _wfinddata_t)); if ( pFileInfo == NULL ) { rc = ERROR_NOT_ENOUGH_MEMORY; break; } } } while ( _wfindnext(hFile, pFileInfo) == 0 ); _findclose(hFile); } if ( pFileInfo != NULL ) { ScepFree(pFileInfo); pFileInfo = NULL; } break; case SE_REGISTRY_KEY: PWSTR Buffer1; HKEY hKey; DWORD index; // // open the key // Buffer1=NULL; rc = ScepOpenRegistryObject( SE_REGISTRY_KEY, ThisNode->ObjectFullName, KEY_READ, &hKey ); if ( rc == ERROR_SUCCESS ) { index = 0; // // enumerate all subkeys of the key // do { Buffer1 = (PWSTR)ScepAlloc(LMEM_ZEROINIT, MAX_PATH*sizeof(WCHAR)); if ( Buffer1 == NULL ) { rc = ERROR_NOT_ENOUGH_MEMORY; break; } BufSize = MAX_PATH; EnumRc = RegEnumKeyEx(hKey, index, Buffer1, &BufSize, NULL, NULL, NULL, NULL); if ( EnumRc == ERROR_SUCCESS ) { index++; // // find if the subkey is already in the tree // if it is in the tree, it will be processed later // for ( pTemp = ThisNode->ChildList, i=-1; pTemp != NULL; pTemp = pTemp->Next ) { if ( pTemp->Node == NULL ) continue; i=_wcsicmp(pTemp->Node->Name, Buffer1); if ( i >= 0 ) break; } if ( pTemp == NULL || i > 0 ) { // // The name is not in the list // BufSize += wcslen(ThisNode->ObjectFullName)+1; Buffer = (PWSTR)ScepAlloc( 0, (BufSize+1)*sizeof(WCHAR)); if ( Buffer == NULL ) { rc = ERROR_NOT_ENOUGH_MEMORY; break; } swprintf(Buffer, L"%s\\%s", ThisNode->ObjectFullName, Buffer1); ScepFree(Buffer1); Buffer1 = NULL; // if ( StartChecking ) { // raise N.C. status even for SCE_STATUS_CHECK if ( StartChecking && ThisNode->Status == SCE_STATUS_OVERWRITE ) { // // do not check owner and group information // rc = ScepAnalyzeObjectAndChildren( Buffer, ObjectType, NULL, (ThisNode->SeInfo & DACL_SECURITY_INFORMATION) | (ThisNode->SeInfo & SACL_SECURITY_INFORMATION) ); } else { /* rc = ScepSaveObjectString( hSection, Buffer, TRUE, SCE_STATUS_NOT_CONFIGURED, NULL, 0 ); */ } if ( rc != ERROR_SUCCESS ) ScepLogOutput3(1, rc, SCEDLL_SAP_ERROR_SECURITY, Buffer); ScepFree(Buffer); Buffer = NULL; if ( rc != ERROR_SUCCESS ) break; } } else if ( EnumRc != ERROR_NO_MORE_ITEMS ) { // // Enumeration shouldn't fail if RegOpenKeyEx works right // because the ENUMERATE_SUB_KEYS access was requested in // the open call. // However, due to a app compat bug (for terminal server) // when a registry key is opened with some access rights, // it is actually opened with MAXIMUM_ALLOWED for certain // keys. This will cause RegEnumKeyEx fail with access denied // error. // In this case, we treat it the same as the open key failed // with the error. // ScepSaveObjectString( hSection, ThisNode->ObjectFullName, TRUE, SCE_STATUS_ERROR_NOT_AVAILABLE, NULL, 0 ); // // skip it // gWarningCode = EnumRc; rc = ERROR_SUCCESS; } if ( Buffer1 != NULL ) { ScepFree(Buffer1); Buffer1 = NULL; } } while ( EnumRc == ERROR_SUCCESS ); RegCloseKey(hKey); if ( EnumRc != ERROR_SUCCESS && EnumRc != ERROR_NO_MORE_ITEMS ) { ScepLogOutput3(1, EnumRc, SCEDLL_SAP_ERROR_ENUMERATE, ThisNode->ObjectFullName); } } else { ScepSaveObjectString( hSection, ThisNode->ObjectFullName, TRUE, SCE_STATUS_ERROR_NOT_AVAILABLE, NULL, 0 ); // // if access is denied or key does not exist, skip it // if ( rc == ERROR_PATH_NOT_FOUND || rc == ERROR_FILE_NOT_FOUND || rc == ERROR_INVALID_HANDLE ) { gWarningCode = rc; rc = ERROR_SUCCESS; } else if ( rc == ERROR_ACCESS_DENIED || rc == ERROR_CANT_ACCESS_FILE || rc == ERROR_SHARING_VIOLATION) { gWarningCode = rc; rc = ERROR_SUCCESS; } } if ( Buffer1 != NULL ) { ScepFree(Buffer1); Buffer1 = NULL; } break; default: break; } if ( Buffer != NULL ) { ScepFree(Buffer); Buffer = NULL; } } if ( rc != ERROR_SUCCESS ) return(rc); ProcChild: // // then process left child // for(pTemp = ThisNode->ChildList; pTemp != NULL; pTemp=pTemp->Next) { if ( pTemp->Node == NULL ) continue; rc = ScepAnalyzeOneObjectInTree( pTemp->Node, ObjectType, Token, GenericMapping ); if ( rc != ERROR_SUCCESS ) { break; } } SkipNode: return(rc); } DWORD ScepAnalyzeObjectOnly( IN PWSTR ObjectFullName, IN BOOL IsContainer, IN SE_OBJECT_TYPE ObjectType, IN PSECURITY_DESCRIPTOR ProfileSD, IN SECURITY_INFORMATION ProfileSeInfo ) /* ++ Routine Description: Get security setting for the current object and compare it with the profile setting. This routine analyzes the current object only. If there is difference in the security setting, the object will be added to the ppObjectChecked object tree to return. Arguments: ObjectFullName - The object's full path name ObjectType - Indicate file object or registry object ProfileSD - security descriptor specified in the template ProfileSeInfo - security information specified in the template Return value: SCESTATUS error codes ++ */ { DWORD Win32rc=NO_ERROR; PSECURITY_DESCRIPTOR pSecurityDescriptor=NULL; // UCHAR psdbuffer[PSD_BASE_LENGTH]; // PISECURITY_DESCRIPTOR psecuritydescriptor = (PISECURITY_DESCRIPTOR) psdbuffer; // ULONG NewBytesNeeded, BytesNeeded; // NTSTATUS NtStatus; // // get security information for this object // // win32 api is too slow!!! Win32rc = GetNamedSecurityInfo( ObjectFullName, ObjectType, ProfileSeInfo, NULL, NULL, NULL, NULL, &pSecurityDescriptor ); /* Win32rc = ScepGetNamedSecurityInfo( ObjectFullName, ObjectType, ProfileSeInfo, &pSecurityDescriptor ); */ if ( Win32rc == ERROR_SUCCESS ) { // // Compare the analysis security descriptor with the profile // Win32rc = ScepCompareAndAddObject( ObjectFullName, ObjectType, IsContainer, pSecurityDescriptor, ProfileSD, ProfileSeInfo, TRUE, NULL ); ScepFree(pSecurityDescriptor); if ( Win32rc != ERROR_SUCCESS ) { ScepLogOutput3(1, Win32rc, SCEDLL_SAP_ERROR_ANALYZE, ObjectFullName); } } else { ScepLogOutput3(1, Win32rc, SCEDLL_ERROR_QUERY_SECURITY, ObjectFullName); } if ( Win32rc != ERROR_SUCCESS ) { ScepSaveObjectString( hSection, ObjectFullName, IsContainer, SCE_STATUS_ERROR_NOT_AVAILABLE, NULL, 0 ); } return(Win32rc); } DWORD ScepGetNamedSecurityInfo( IN PWSTR ObjectFullName, IN SE_OBJECT_TYPE ObjectType, IN SECURITY_INFORMATION ProfileSeInfo, OUT PSECURITY_DESCRIPTOR *ppSecurityDescriptor ) { DWORD Win32rc=ERROR_INVALID_PARAMETER; HANDLE Handle=NULL; *ppSecurityDescriptor = NULL; switch ( ObjectType ) { case SE_FILE_OBJECT: Win32rc = ScepOpenFileObject( (LPWSTR)ObjectFullName, ScepGetDesiredAccess(READ_ACCESS_RIGHTS, ProfileSeInfo), &Handle ); if (Win32rc == ERROR_SUCCESS ) { Win32rc = ScepGetFileSecurityInfo( Handle, ProfileSeInfo, ppSecurityDescriptor); CloseHandle(Handle); } break; case SE_REGISTRY_KEY: #ifdef _WIN64 case SE_REGISTRY_WOW64_32KEY: #endif Win32rc = ScepOpenRegistryObject( ObjectType, (LPWSTR)ObjectFullName, ScepGetDesiredAccess(READ_ACCESS_RIGHTS, ProfileSeInfo), (PHKEY)&Handle ); if (Win32rc == ERROR_SUCCESS ) { Win32rc = ScepGetKeySecurityInfo( (HKEY)Handle, ProfileSeInfo, ppSecurityDescriptor); RegCloseKey((HKEY)Handle); } break; } if ( Win32rc != NO_ERROR && *ppSecurityDescriptor != NULL ) { ScepFree(*ppSecurityDescriptor); *ppSecurityDescriptor = NULL; } return(Win32rc); } DWORD ScepGetFileSecurityInfo( IN HANDLE Handle, IN SECURITY_INFORMATION SecurityInfo, OUT PSECURITY_DESCRIPTOR * pSecurityDescriptor ) /* ++ A modified copy of GetKernelSecurityInfo from windows\base\accctrl\kernel.cxx -- */ { UCHAR psdbuffer[PSD_BASE_LENGTH]; PISECURITY_DESCRIPTOR psecuritydescriptor = (PISECURITY_DESCRIPTOR) psdbuffer; DWORD status = NO_ERROR; NTSTATUS ntstatus; ULONG bytesneeded = 0; ULONG newbytesneeded; if ( !NT_SUCCESS(ntstatus = NtQuerySecurityObject( Handle, SecurityInfo, psecuritydescriptor, PSD_BASE_LENGTH, &bytesneeded))) { if (STATUS_BUFFER_TOO_SMALL == ntstatus) { if (NULL == (psecuritydescriptor = (PISECURITY_DESCRIPTOR) ScepAlloc( LMEM_ZEROINIT, bytesneeded))) { return(ERROR_NOT_ENOUGH_MEMORY); } else { if ( !NT_SUCCESS(ntstatus = NtQuerySecurityObject(Handle, SecurityInfo, psecuritydescriptor, bytesneeded, &newbytesneeded))) { status = RtlNtStatusToDosError(ntstatus); } } } else { status = RtlNtStatusToDosError(ntstatus); } } if (NO_ERROR == status) { status = ScepGetSecurityDescriptorParts( psecuritydescriptor, SecurityInfo, pSecurityDescriptor); } // if (bytesneeded > PSD_BASE_LENGTH) { if ( psecuritydescriptor != (PISECURITY_DESCRIPTOR)psdbuffer ) { ScepFree(psecuritydescriptor); } return(status); } DWORD ScepGetSecurityDescriptorParts( IN PISECURITY_DESCRIPTOR pSecurityDescriptor, IN SECURITY_INFORMATION SecurityInfo, OUT PSECURITY_DESCRIPTOR *pOutSecurityDescriptor ) /* ++ A modified copy of GetSecurityDescriptorParts from windows\base\accctrl\src\common.cxx -- */ { NTSTATUS ntstatus; DWORD status = NO_ERROR; PSID owner = NULL, group = NULL; PACL dacl = NULL, sacl = NULL; ULONG csize = sizeof(SECURITY_DESCRIPTOR); BOOLEAN bDummy, bParmPresent = FALSE; PISECURITY_DESCRIPTOR poutsd; PVOID bufptr=NULL; SECURITY_DESCRIPTOR_CONTROL theControl=0; ULONG theRevision=0; // // if no security descriptor found, don't return one! // *pOutSecurityDescriptor = NULL; if ( pSecurityDescriptor ) { // // if the security descriptor is self relative, get absolute // pointers to the components // ntstatus = RtlGetOwnerSecurityDescriptor( pSecurityDescriptor, &owner, &bDummy); if (NT_SUCCESS(ntstatus)) { ntstatus = RtlGetGroupSecurityDescriptor( pSecurityDescriptor, &group, &bDummy); } if (NT_SUCCESS(ntstatus)) { ntstatus = RtlGetDaclSecurityDescriptor( pSecurityDescriptor, &bParmPresent, &dacl, &bDummy); if (NT_SUCCESS(ntstatus) && !bParmPresent) dacl = NULL; } if (NT_SUCCESS(ntstatus)) { ntstatus = RtlGetSaclSecurityDescriptor( pSecurityDescriptor, &bParmPresent, &sacl, &bDummy); if (NT_SUCCESS(ntstatus) && !bParmPresent) sacl = NULL; } if (NT_SUCCESS(ntstatus)) { // // Build the new security descriptor // csize = RtlLengthSecurityDescriptor( pSecurityDescriptor ) + sizeof(SECURITY_DESCRIPTOR) - sizeof(SECURITY_DESCRIPTOR_RELATIVE); // // There is size difference in relative form and absolute form // on 64 bit system. - always add the difference to the size. // This has no effect on 32bit system. On 64bit system, if the input // security descriptor is in absolute form already, we will waste // 16 bytes per security descriptor. // // Another option is to detect the form of the input security descriptor // but that takes a pointer deref, a & operation, and code complexity. // Plus, it will affect performance of 32bit system. The output SD // will be freed after a short period of time, so we go with // the first option. // if (NULL == (poutsd = (PISECURITY_DESCRIPTOR)ScepAlloc(LMEM_ZEROINIT, csize))) return(ERROR_NOT_ENOUGH_MEMORY); RtlCreateSecurityDescriptor(poutsd, SECURITY_DESCRIPTOR_REVISION); ntstatus = RtlGetControlSecurityDescriptor ( pSecurityDescriptor, &theControl, &theRevision ); if ( NT_SUCCESS(ntstatus) ) { theControl &= SE_VALID_CONTROL_BITS; RtlSetControlSecurityDescriptor ( poutsd, theControl, theControl ); } ntstatus = STATUS_SUCCESS; bufptr = Add2Ptr(poutsd, sizeof(SECURITY_DESCRIPTOR)); if (SecurityInfo & OWNER_SECURITY_INFORMATION) { if (NULL != owner) { // // no error checking as these should not fail!! // ntstatus = RtlCopySid(RtlLengthSid(owner), (PSID)bufptr, owner); if ( NT_SUCCESS(ntstatus) ) { ntstatus = RtlSetOwnerSecurityDescriptor(poutsd, (PSID)bufptr, FALSE); if ( NT_SUCCESS(ntstatus) ) bufptr = Add2Ptr(bufptr,RtlLengthSid(owner)); } } else ntstatus = STATUS_NO_SECURITY_ON_OBJECT; } if (NT_SUCCESS(ntstatus) && (SecurityInfo & GROUP_SECURITY_INFORMATION) ) { if (NULL != group) { // // no error checking as these should not fail!! // ntstatus = RtlCopySid(RtlLengthSid(group), (PSID)bufptr, group); if ( NT_SUCCESS(ntstatus) ) { ntstatus = RtlSetGroupSecurityDescriptor(poutsd, (PSID)bufptr, FALSE); if ( NT_SUCCESS(ntstatus) ) bufptr = Add2Ptr(bufptr,RtlLengthSid(group)); } } else ntstatus = STATUS_NO_SECURITY_ON_OBJECT; } // // The DACL and SACL may or may not be on the object. // if ( NT_SUCCESS(ntstatus) && (SecurityInfo & DACL_SECURITY_INFORMATION) ) { if (NULL != dacl) { RtlCopyMemory(bufptr, dacl, dacl->AclSize); ntstatus = RtlSetDaclSecurityDescriptor(poutsd, TRUE, (ACL *)bufptr, FALSE); if ( NT_SUCCESS(ntstatus) ) bufptr = Add2Ptr(bufptr, dacl->AclSize); } } if ( NT_SUCCESS(ntstatus) && (SecurityInfo & SACL_SECURITY_INFORMATION)){ if (NULL != sacl) { RtlCopyMemory(bufptr, sacl, sacl->AclSize); ntstatus = RtlSetSaclSecurityDescriptor(poutsd, TRUE, (ACL *)bufptr, FALSE); } } if (!NT_SUCCESS(ntstatus)) ScepFree(poutsd); else *pOutSecurityDescriptor = poutsd; } status = RtlNtStatusToDosError(ntstatus); } return(status); } DWORD ScepGetKeySecurityInfo( IN HANDLE Handle, IN SECURITY_INFORMATION SecurityInfo, OUT PSECURITY_DESCRIPTOR *pSecurityDescriptor ) /* ++ A modified copy of GetRegistrySecurityInfo in windows\base\accctrl\src\registry.cxx -- */ { if ( SecurityInfo == 0 || pSecurityDescriptor == NULL ) return ERROR_INVALID_PARAMETER; UCHAR psdbuffer[PSD_BASE_LENGTH]; PISECURITY_DESCRIPTOR psecuritydescriptor = (PISECURITY_DESCRIPTOR) psdbuffer; DWORD status; ULONG bytesneeded = PSD_BASE_LENGTH; if ( NO_ERROR != (status = RegGetKeySecurity( (HKEY)Handle, SecurityInfo, psecuritydescriptor, &bytesneeded) ) ) { if (ERROR_INSUFFICIENT_BUFFER == status) { if (NULL == (psecuritydescriptor = (PISECURITY_DESCRIPTOR) ScepAlloc(LMEM_ZEROINIT, bytesneeded))) { return(ERROR_NOT_ENOUGH_MEMORY); } else { status = RegGetKeySecurity((HKEY)Handle, SecurityInfo, psecuritydescriptor, &bytesneeded); } } } if (NO_ERROR == status) { status = ScepGetSecurityDescriptorParts( psecuritydescriptor, SecurityInfo, pSecurityDescriptor); } // if (bytesneeded > PSD_BASE_LENGTH) if ( psecuritydescriptor != (PISECURITY_DESCRIPTOR)psdbuffer ) ScepFree(psecuritydescriptor); return(status); } DWORD ScepCompareAndAddObject( IN PWSTR ObjectFullName, IN SE_OBJECT_TYPE ObjectType, IN BOOL IsContainer, IN PSECURITY_DESCRIPTOR pSecurityDescriptor, IN PSECURITY_DESCRIPTOR ProfileSD, IN SECURITY_INFORMATION ProfileSeInfo, IN BOOL AddObject, OUT PBYTE IsDifferent OPTIONAL ) /* ++ Routine Description: Compare two security descriptors and add the object to the analysis database if there is difference. Arguments: ObjectFullName - The object's full path name pSecurityDescriptor - The security descriptor of current object's setting ProfileSD - security descriptor specified in the template ProfileSeInfo - security information specified in the template Return value: SCESTATUS error codes ++ */ { DWORD rc; BYTE Status; PWSTR SDspec=NULL; DWORD SDsize; rc = ScepCompareObjectSecurity(ObjectType, IsContainer, pSecurityDescriptor, ProfileSD, ProfileSeInfo, &Status ); if ( NO_ERROR == rc ) { if ( AddObject && Status && ObjectFullName != NULL ) { // // save this one in SAP section // rc = ConvertSecurityDescriptorToText( pSecurityDescriptor, ProfileSeInfo, &SDspec, &SDsize ); if ( rc == ERROR_SUCCESS ) { // // Save to the SAP section // if ( ObjectType == SE_DS_OBJECT ) { rc = ScepSaveDsStatusToSection( ObjectFullName, IsContainer, Status, SDspec, SDsize ); } else { rc = ScepSaveObjectString( hSection, ObjectFullName, IsContainer, Status, SDspec, SDsize ); } if ( SCESTATUS_OBJECT_EXIST == rc ) { ScepLogOutput3(1, ERROR_FILE_EXISTS, SCEDLL_SAP_ERROR_SAVE, ObjectFullName); rc = ERROR_SUCCESS; } else if ( rc != SCESTATUS_SUCCESS ) rc = ScepSceStatusToDosError(rc); } else ScepLogOutput3(1, rc, SCEDLL_SAP_ERROR_SAVE, ObjectFullName); if ( SDspec != NULL ) ScepFree(SDspec); BadCnt++; } if ( IsDifferent ) { *IsDifferent = Status; } } else { if ( ObjectFullName ) { ScepLogOutput3(1, rc, SCEDLL_SAP_ERROR_ACL, ObjectFullName); } } return(rc); } DWORD ScepAnalyzeObjectAndChildren( IN PWSTR ObjectFullName, IN SE_OBJECT_TYPE ObjectType, IN PSECURITY_DESCRIPTOR ProfileSD, IN SECURITY_INFORMATION ProfileSeInfo ) /* ++ Routine Description: Analyze current object and all subkeys/files/directories under the object. If there is difference in security setting for any object, the object will be added to the analysis database. Arguments: ObjectFullName - The object's full path name ObjectType - Indicate file object or registry object ProfileSD - security descriptor specified in the template ProfileSeInfo - security information specified in the template Return value: SCESTATUS error codes ++ */ { DWORD rc=0; PWSTR Buffer=NULL; intptr_t hFile; struct _wfinddata_t * pFileInfo=NULL; DWORD index; DWORD BufSize; PWSTR Buffer1=NULL; DWORD EnumRc=0; HKEY hKey; DWORD ObjectLen; // // analyze this file/key first // rc = ScepAnalyzeObjectOnly( ObjectFullName, TRUE, ObjectType, ProfileSD, ProfileSeInfo ); // // if the object denies access or does not exist, skip it. // if ( rc == ERROR_ACCESS_DENIED || rc == ERROR_CANT_ACCESS_FILE || rc == ERROR_SHARING_VIOLATION) { gWarningCode = rc; rc = ScepSaveObjectString( hSection, ObjectFullName, TRUE, SCE_STATUS_ERROR_NOT_AVAILABLE, NULL, 0 ); return(rc); } else if ( rc == ERROR_FILE_NOT_FOUND || rc == ERROR_PATH_NOT_FOUND || rc == ERROR_INVALID_HANDLE ) { gWarningCode = rc; return(SCESTATUS_SUCCESS); } if ( rc != ERROR_SUCCESS ) return(rc); // // recursively analyze all children under this file/key // ObjectLen = wcslen(ObjectFullName); switch ( ObjectType ) { case SE_FILE_OBJECT: pFileInfo = (struct _wfinddata_t *)ScepAlloc(0,sizeof(struct _wfinddata_t)); if ( pFileInfo == NULL ) { rc = ERROR_NOT_ENOUGH_MEMORY; break; } // // find all files under this directory/file // BufSize = ObjectLen+4; Buffer = (PWSTR)ScepAlloc( 0, (BufSize+1)*sizeof(WCHAR)); if ( Buffer == NULL ) { rc = ERROR_NOT_ENOUGH_MEMORY; break; } swprintf(Buffer, L"%s\\*.*", ObjectFullName); hFile = _wfindfirst(Buffer, pFileInfo); ScepFree(Buffer); Buffer = NULL; if ( hFile != -1 && ( 0 == ( GetFileAttributes(ObjectFullName) & // bug 635098: don't propagate permissions FILE_ATTRIBUTE_REPARSE_POINT ))) // across junction points { do { if ( wcscmp(L"..", pFileInfo->name) == 0 || wcscmp(L".", pFileInfo->name) == 0 ) continue; // // The name is not in the list, so analyze this one // BufSize = ObjectLen+wcslen(pFileInfo->name)+1; Buffer = (PWSTR)ScepAlloc( 0, (BufSize+1)*sizeof(WCHAR)); if ( Buffer == NULL ) { rc = ERROR_NOT_ENOUGH_MEMORY; break; } swprintf(Buffer, L"%s\\%s", ObjectFullName, pFileInfo->name); EnumRc = pFileInfo->attrib; // use this variable temp ScepFree(pFileInfo); pFileInfo = NULL; if ( EnumRc & _A_SUBDIR ) { rc = ScepAnalyzeObjectAndChildren( Buffer, ObjectType, ProfileSD, ProfileSeInfo ); } else { rc = ScepAnalyzeObjectOnly( Buffer, FALSE, ObjectType, ProfileSD, ProfileSeInfo ); } ScepFree(Buffer); Buffer = NULL; if ( rc != ERROR_SUCCESS ) break; pFileInfo = (struct _wfinddata_t *)ScepAlloc(0,sizeof(struct _wfinddata_t)); if ( pFileInfo == NULL ) { rc = ERROR_NOT_ENOUGH_MEMORY; break; } } while ( _wfindnext(hFile, pFileInfo) == 0 ); _findclose(hFile); } break; case SE_REGISTRY_KEY: // // open the key // rc = ScepOpenRegistryObject( SE_REGISTRY_KEY, ObjectFullName, KEY_READ, &hKey ); if ( rc == ERROR_SUCCESS ) { index = 0; // // enumerate all subkeys of the key // do { Buffer1 = (PWSTR)ScepAlloc(LMEM_ZEROINIT, MAX_PATH*sizeof(WCHAR)); if ( Buffer1 == NULL ) { rc = ERROR_NOT_ENOUGH_MEMORY; break; } BufSize = MAX_PATH; EnumRc = RegEnumKeyEx(hKey, index, Buffer1, &BufSize, NULL, NULL, NULL, NULL); if ( EnumRc == ERROR_SUCCESS ) { index++; // // analyze children under this key // BufSize += ObjectLen+1; Buffer = (PWSTR)ScepAlloc( 0, (BufSize+1)*sizeof(WCHAR)); if ( Buffer == NULL ) { rc = ERROR_NOT_ENOUGH_MEMORY; break; } swprintf(Buffer, L"%s\\%s", ObjectFullName, Buffer1); ScepFree(Buffer1); Buffer1 = NULL; rc = ScepAnalyzeObjectAndChildren( Buffer, ObjectType, ProfileSD, ProfileSeInfo ); ScepFree(Buffer); Buffer = NULL; if ( rc != ERROR_SUCCESS ) break; } else if ( EnumRc != ERROR_NO_MORE_ITEMS ) { // // Enumeration shouldn't fail if RegOpenKeyEx works right // because the ENUMERATE_SUB_KEYS access was requested in // the open call. // However, due to a app compat bug (for terminal server) // when a registry key is opened with some access rights, // it is actually opened with MAXIMUM_ALLOWED for certain // keys. This will cause RegEnumKeyEx fail with access denied // error. // In this case, we treat it the same as the open key failed // with the error. // // skip it // rc = EnumRc; } if ( Buffer1 != NULL ) { ScepFree(Buffer1); Buffer1 = NULL; } } while ( EnumRc == ERROR_SUCCESS ); RegCloseKey(hKey); if ( EnumRc != ERROR_SUCCESS && EnumRc != ERROR_NO_MORE_ITEMS ) { ScepLogOutput3(1, EnumRc, SCEDLL_SAP_ERROR_ENUMERATE, ObjectFullName); rc = EnumRc; } } else ScepLogOutput3(1, rc, SCEDLL_ERROR_OPEN, ObjectFullName); if ( rc == ERROR_ACCESS_DENIED || rc == ERROR_CANT_ACCESS_FILE || rc == ERROR_SHARING_VIOLATION) { gWarningCode = rc; rc = ScepSaveObjectString( hSection, ObjectFullName, TRUE, SCE_STATUS_ERROR_NOT_AVAILABLE, NULL, 0 ); } else if ( rc == ERROR_PATH_NOT_FOUND || rc == ERROR_FILE_NOT_FOUND || rc == ERROR_INVALID_HANDLE ) { gWarningCode = rc; rc = SCESTATUS_SUCCESS; } break; default: break; } if ( Buffer != NULL ) { ScepFree(Buffer); } if ( Buffer1 != NULL ) { ScepFree(Buffer1); } if ( pFileInfo != NULL ) { ScepFree(pFileInfo); } return(rc); } SCESTATUS ScepAnalyzeSystemAuditing( IN PSCE_PROFILE_INFO pSmpInfo, IN PPOLICY_AUDIT_EVENTS_INFO AuditEvent ) /* ++ Routine Description: This routine queries the system auditing security which includes event log setting, audit event setting, SACL for registry, and SACL for files. Arguments: SnapShot - The buffer which contains analyzed system info Return value: SCESTATUS_SUCCESS SCESTATUS_NOT_ENOUGH_MEMORY SCESTATUS_INVALID_PARAMETER SCESTATUS_OTHER_ERROR -- */ { SCESTATUS rc; DWORD i,retRc=NO_ERROR; WCHAR MsgBuf[256]; PCWSTR szAuditSection=NULL; DWORD MaxSize=0; DWORD Retention=0; DWORD AuditLogRetentionPeriod; DWORD RetentionDays; DWORD RestrictGuest; NTSTATUS NtStatus; LSA_HANDLE LsaHandle=NULL; PPOLICY_AUDIT_FULL_QUERY_INFO AuditFull=NULL; BOOL bSaveSnapshot=FALSE; if ( (gOptions & SCE_NO_ANALYZE) || (gOptions & SCE_GENERATE_ROLLBACK) ) bSaveSnapshot = TRUE; if ( !(gOptions & SCE_NO_ANALYZE) ) { // // DO NOT query log settings in system upgrade because local policy // table doesn't keep log settings // only analyze log settings // Audit Log settings are saved in the Registry // under System\CurrentControlSet\Services\EventLog\\MaxSize and Retention // for ( i=0; i<3; i++) { // // Different logs have different keys in Registry // switch (i) { case 0: wcscpy(MsgBuf,L"System\\CurrentControlSet\\Services\\EventLog\\System"); szAuditSection = szAuditSystemLog; break; case 1: wcscpy(MsgBuf,L"System\\CurrentControlSet\\Services\\EventLog\\Security"); szAuditSection = szAuditSecurityLog; break; default: wcscpy(MsgBuf,L"System\\CurrentControlSet\\Services\\EventLog\\Application"); szAuditSection = szAuditApplicationLog; break; } // // Preprea the section to write to // rc = ScepStartANewSection( hProfile, &hSection, bSaveSnapshot ? SCEJET_TABLE_SMP : SCEJET_TABLE_SAP, szAuditSection ); if ( rc != SCESTATUS_SUCCESS ) { retRc = ScepSceStatusToDosError(rc); ScepLogOutput3(1, retRc, SCEDLL_SAP_START_SECTION, (PWSTR)szAuditSection); continue; } DWORD LogValues[MAX_LOG_ITEMS]; for ( MaxSize=0; MaxSizeMaximumLogSize[i], MaxSize ); if ( rc == SCESTATUS_SUCCESS ) { LogValues[IDX_MAX_LOG_SIZE] = MaxSize; switch ( Retention ) { case MAXULONG: // manually AuditLogRetentionPeriod = 2; RetentionDays = SCE_NO_VALUE; break; case 0: AuditLogRetentionPeriod = 0; RetentionDays = SCE_NO_VALUE; break; default: AuditLogRetentionPeriod = 1; // number of days * seconds/day RetentionDays = Retention / (24 * 3600); break; } BOOL bReplaceOnly=FALSE; if ( (gOptions & SCE_GENERATE_ROLLBACK) && (pSmpInfo->AuditLogRetentionPeriod[i] == SCE_NO_VALUE) && (pSmpInfo->RetentionDays[i] == SCE_NO_VALUE) ) { bReplaceOnly = TRUE; } rc = ScepCompareAndSaveIntValue( hSection, L"AuditLogRetentionPeriod", bReplaceOnly, (gOptions & SCE_NO_ANALYZE) ? SCE_SNAPSHOT_VALUE : pSmpInfo->AuditLogRetentionPeriod[i], AuditLogRetentionPeriod ); if ( rc == SCESTATUS_SUCCESS ) { LogValues[IDX_RET_PERIOD] = AuditLogRetentionPeriod; if ( RetentionDays != SCE_NO_VALUE ) { rc = ScepCompareAndSaveIntValue( hSection, L"RetentionDays", bReplaceOnly, (gOptions & SCE_NO_ANALYZE) ? SCE_SNAPSHOT_VALUE : pSmpInfo->RetentionDays[i], RetentionDays ); } if ( rc == SCESTATUS_SUCCESS ) { LogValues[IDX_RET_DAYS] = RetentionDays; rc = ScepCompareAndSaveIntValue( hSection, L"RestrictGuestAccess", (gOptions & SCE_GENERATE_ROLLBACK), (gOptions & SCE_NO_ANALYZE) ? SCE_SNAPSHOT_VALUE : pSmpInfo->RestrictGuestAccess[i], RestrictGuest ); if ( rc == SCESTATUS_SUCCESS ) { LogValues[IDX_RESTRICT_GUEST] = RestrictGuest; } } } } rc = ScepSceStatusToDosError(rc); } else { ScepLogOutput3( 1, rc, SCEDLL_ERROR_QUERY_LOGSETTINGS); } if ( rc != NO_ERROR && !(gOptions & SCE_GENERATE_ROLLBACK) ) { // // see if anything should be raised as error // for ( MaxSize=0; MaxSizeAuditingMode ) { for ( i=0; iMaximumAuditEventCount; i++ ) AuditEvent->EventAuditingOptions[i] = 0; } rc = ScepCompareAndSaveIntValue( hSection, L"AuditSystemEvents", (gOptions & SCE_GENERATE_ROLLBACK), (gOptions & SCE_NO_ANALYZE) ? SCE_SNAPSHOT_VALUE : pSmpInfo->AuditSystemEvents, AuditEvent->EventAuditingOptions[AuditCategorySystem] ); if ( rc == SCESTATUS_SUCCESS ) { EventValues[IDX_AUDIT_SYSTEM] = 1; rc = ScepCompareAndSaveIntValue( hSection, L"AuditLogonEvents", (gOptions & SCE_GENERATE_ROLLBACK), (gOptions & SCE_NO_ANALYZE) ? SCE_SNAPSHOT_VALUE : pSmpInfo->AuditLogonEvents, AuditEvent->EventAuditingOptions[AuditCategoryLogon] ); if ( rc == SCESTATUS_SUCCESS ) { EventValues[IDX_AUDIT_LOGON] = 1; rc = ScepCompareAndSaveIntValue( hSection, L"AuditObjectAccess", (gOptions & SCE_GENERATE_ROLLBACK), (gOptions & SCE_NO_ANALYZE) ? SCE_SNAPSHOT_VALUE : pSmpInfo->AuditObjectAccess, AuditEvent->EventAuditingOptions[AuditCategoryObjectAccess] ); if ( rc == SCESTATUS_SUCCESS ) { EventValues[IDX_AUDIT_OBJECT] = 1; rc = ScepCompareAndSaveIntValue( hSection, L"AuditPrivilegeUse", (gOptions & SCE_GENERATE_ROLLBACK), (gOptions & SCE_NO_ANALYZE) ? SCE_SNAPSHOT_VALUE : pSmpInfo->AuditPrivilegeUse, AuditEvent->EventAuditingOptions[AuditCategoryPrivilegeUse] ); if ( rc == SCESTATUS_SUCCESS ) { EventValues[IDX_AUDIT_PRIV] = 1; rc = ScepCompareAndSaveIntValue( hSection, L"AuditProcessTracking", (gOptions & SCE_GENERATE_ROLLBACK), (gOptions & SCE_NO_ANALYZE) ? SCE_SNAPSHOT_VALUE : pSmpInfo->AuditProcessTracking, AuditEvent->EventAuditingOptions[AuditCategoryDetailedTracking] ); if ( rc == SCESTATUS_SUCCESS ) { EventValues[IDX_AUDIT_PROCESS] = 1; rc = ScepCompareAndSaveIntValue( hSection, L"AuditPolicyChange", (gOptions & SCE_GENERATE_ROLLBACK), (gOptions & SCE_NO_ANALYZE) ? SCE_SNAPSHOT_VALUE : pSmpInfo->AuditPolicyChange, AuditEvent->EventAuditingOptions[AuditCategoryPolicyChange] ); if ( rc == SCESTATUS_SUCCESS ) { EventValues[IDX_AUDIT_ACCOUNT] = 1; rc = ScepCompareAndSaveIntValue( hSection, L"AuditAccountManage", (gOptions & SCE_GENERATE_ROLLBACK), (gOptions & SCE_NO_ANALYZE) ? SCE_SNAPSHOT_VALUE : pSmpInfo->AuditAccountManage, AuditEvent->EventAuditingOptions[AuditCategoryAccountManagement] ); if ( rc == SCESTATUS_SUCCESS ) { EventValues[IDX_AUDIT_DS] = 1; rc = ScepCompareAndSaveIntValue( hSection, L"AuditDSAccess", (gOptions & SCE_GENERATE_ROLLBACK), (gOptions & SCE_NO_ANALYZE) ? SCE_SNAPSHOT_VALUE : pSmpInfo->AuditDSAccess, AuditEvent->EventAuditingOptions[AuditCategoryDirectoryServiceAccess] ); if ( rc == SCESTATUS_SUCCESS ) { EventValues[IDX_AUDIT_ACCT_LOGON] = 1; rc = ScepCompareAndSaveIntValue( hSection, L"AuditAccountLogon", (gOptions & SCE_GENERATE_ROLLBACK), (gOptions & SCE_NO_ANALYZE) ? SCE_SNAPSHOT_VALUE : pSmpInfo->AuditAccountLogon, AuditEvent->EventAuditingOptions[AuditCategoryAccountLogon] ); } } } } } } } } } else { // // audit policy is not available // rc = SCESTATUS_ACCESS_DENIED; } if ( rc != SCESTATUS_SUCCESS ) { rc = ScepSceStatusToDosError(rc); ScepLogOutput3( 1, rc, SCEDLL_SAP_ERROR_EVENT_AUDITING); } else { ScepLogOutput3( 1, 0, SCEDLL_SAP_EVENT_AUDITING); } if ( rc != NO_ERROR ) { retRc = rc; if ( !bSaveSnapshot ) { for ( MaxSize=0; MaxSizeJetSessionID != JET_sesidNil) && (hProfile->JetDbID != JET_dbidNil) && !(Options & SCE_GENERATE_ROLLBACK) ) { NtStatus = NtQuerySystemTime(&CurrentTime); if ( NtStatus == STATUS_SUCCESS ) { //printf("TimeStamp: %x%x\n", CurrentTime.HighPart, CurrentTime.LowPart); Status = SceJetSetTimeStamp( hProfile, TRUE, CurrentTime ); if ( Status != SCESTATUS_SUCCESS ) ScepLogOutput3(1, ScepSceStatusToDosError(Status), SCEDLL_TIMESTAMP_ERROR, L"SAP"); } else ScepLogOutput3(1, RtlNtStatusToDosError(NtStatus), SCEDLL_TIMESTAMP_ERROR, L"SAP"); } // // recover the SAP profile if errors // if (rc != SCESTATUS_SUCCESS) { // // rollback all changes // // SceJetRollback( hProfile, 0 ); ScepLogOutput3(0, ScepSceStatusToDosError(rc), SCEDLL_SAP_ERROR_OUT); } else { // // Commit the transaction // // SceJetCommitTransaction( hProfile, 0 ); } gTotalTicks = 0; gCurrentTicks = 0; gWarningCode = 0; cbClientFlag = 0; gOptions = 0; // // Close the JET section and profile handle // if ( hSection != NULL ) SceJetCloseSection( &hSection, TRUE ); hSection = NULL; if ( hProfile != NULL ) SceJetCloseFile( hProfile, TRUE, FALSE ); hProfile = NULL; return( SCESTATUS_SUCCESS ); } DWORD ScepSaveDsStatusToSection( IN PWSTR ObjectName, IN BOOL IsContainer, IN BYTE Flag, IN PWSTR Value, IN DWORD ValueLen ) { SCESTATUS rc; PWSTR SaveName; rc = ScepConvertLdapToJetIndexName( ObjectName, &SaveName ); if ( rc == SCESTATUS_SUCCESS ) { rc = ScepSaveObjectString( hSection, SaveName, IsContainer, Flag, Value, ValueLen ); ScepFree(SaveName); } return(ScepSceStatusToDosError(rc)); } //**************************************************** // migration functions // SCESTATUS ScepMigrateDatabase( IN PSCECONTEXT cxtProfile, IN BOOL bSystemDb ) { SCESTATUS rc; FLOAT CurrentVersion; rc = SceJetCheckVersion( cxtProfile, &CurrentVersion ); ScepPostProgress(0, 0, NULL); if ( rc == SCESTATUS_BAD_FORMAT ) { if ( CurrentVersion < (FLOAT)1.2 ) { rc = SCESTATUS_SUCCESS; if ( bSystemDb ) { // // just simply delete the local policy and SAP table // ignore error occured (e.g, table doesn't exist) // ScepDeleteInfoForAreas( cxtProfile, SCE_ENGINE_SMP, AREA_ALL ); SceJetDeleteTable( cxtProfile, "SmTblSap", SCEJET_TABLE_SAP ); ScepPostProgress(4*TICKS_MIGRATION_SECTION, 0, NULL); } else { // // database format is wrong, migrate it // Version 1.1: it only contains SDDL syntax change // which applies to file/key/DS object/services (general) // Version 1.2: table format change (SCP, GPO), more columns // // service extensions should handle their changes respectively // should be handled by WMI schema // if ( CurrentVersion != (FLOAT)1.1 ) { rc = ScepMigrateDatabaseRevision0( cxtProfile ); } else { ScepPostProgress(4*TICKS_MIGRATION_SECTION, 0, NULL); } } if ( SCESTATUS_SUCCESS == rc ) { rc = ScepMigrateDatabaseRevision1( cxtProfile ); } ScepPostProgress(TICKS_MIGRATION_V11, 0, NULL); } else if ( CurrentVersion == (FLOAT)1.2 ) { // // current version, no need to migrate // } // else newer version, BAD_FORMAT } return rc; } SCESTATUS ScepMigrateDatabaseRevision0( IN PSCECONTEXT cxtProfile ) { SCESTATUS rc; DWORD nTickedSection=0; rc = ScepMigrateObjectSection( cxtProfile, szFileSecurity ); ScepPostProgress(TICKS_MIGRATION_SECTION, 0, NULL); nTickedSection++; if ( rc == SCESTATUS_SUCCESS ) { rc = ScepMigrateObjectSection( cxtProfile, szRegistryKeys ); ScepPostProgress(TICKS_MIGRATION_SECTION, 0, NULL); nTickedSection++; if ( rc == SCESTATUS_SUCCESS ) { rc = ScepMigrateObjectSection( cxtProfile, szDSSecurity ); ScepPostProgress(TICKS_MIGRATION_SECTION, 0, NULL); nTickedSection++; if ( rc == SCESTATUS_SUCCESS ) { rc = ScepMigrateObjectSection( cxtProfile, szServiceGeneral ); if ( rc == SCESTATUS_SUCCESS ) { // // delete everything in SCP // SceJetDeleteAll( cxtProfile, "SmTblScp", SCEJET_TABLE_SCP ); // // change version number to 1.1 now // FLOAT Version = (FLOAT)1.1; rc = SceJetSetValueInVersion( cxtProfile, "SmTblVersion", "Version", (PWSTR)(&Version), //(PWSTR)CharTimeStamp, 4, // number of bytes JET_prepReplace ); } ScepPostProgress(TICKS_MIGRATION_SECTION, 0, NULL); nTickedSection++; } } } if ( nTickedSection < 4 ) { ScepPostProgress((4-nTickedSection)*TICKS_MIGRATION_SECTION, 0, NULL); } return rc; } SCESTATUS ScepMigrateObjectSection( IN PSCECONTEXT cxtProfile, IN PCWSTR szSection ) { // // should update all three tables (SCP/SMP/SAP) // we could just wipe SCP/SAP when SCP is used for accumulated policy // DOUBLE SectionID = 0; SCESTATUS rc; // // get section id // rc = SceJetGetSectionIDByName( cxtProfile, szSection, &SectionID ); if ( rc == SCESTATUS_RECORD_NOT_FOUND ) { rc = SCESTATUS_SUCCESS; } else if ( rc != SCESTATUS_SUCCESS ) return(rc); PSCESECTION h_Section = NULL; if ( cxtProfile->JetSmpID != JET_tableidNil ) { // // SMP table // rc = SceJetOpenSection( cxtProfile, SectionID, SCEJET_TABLE_SMP, &h_Section ); if ( SCESTATUS_SUCCESS == rc ) { rc = ScepMigrateOneSection(h_Section); } if ( rc == SCESTATUS_RECORD_NOT_FOUND ) { rc = SCESTATUS_SUCCESS; } } if ( SCESTATUS_SUCCESS == rc && cxtProfile->JetSapID != JET_tableidNil ) { // // SAP table // rc = SceJetOpenSection( cxtProfile, SectionID, SCEJET_TABLE_SAP, &h_Section ); if ( SCESTATUS_SUCCESS == rc ) { rc = ScepMigrateOneSection(h_Section); } if ( rc == SCESTATUS_RECORD_NOT_FOUND ) { rc = SCESTATUS_SUCCESS; } } // // SCP table is now pointing to SMP table, no need to migrate // all contents in SCP table should be deleted and repropped // by policy propagation // if ( h_Section ) { SceJetCloseSection(&h_Section, TRUE); } return rc; } SCESTATUS ScepMigrateOneSection( PSCESECTION hSection ) { SCESTATUS rc; // // goto the first line of this section // rc = SceJetSeek( hSection, NULL, 0, SCEJET_SEEK_GE ); PWSTR pszValue=NULL; DWORD ValueSize=0; DWORD NewSize; while ( rc == SCESTATUS_SUCCESS ) { rc = SceJetGetValue( hSection, SCEJET_CURRENT, NULL, NULL, 0, NULL, NULL, 0, &ValueSize ); if ( SCESTATUS_SUCCESS == rc ) { pszValue = (PWSTR)LocalAlloc(LPTR, ValueSize+sizeof(WCHAR)); if ( pszValue ) { // // get the value // rc = SceJetGetValue( hSection, SCEJET_CURRENT, NULL, NULL, 0, NULL, pszValue, ValueSize, &NewSize ); if ( SCESTATUS_SUCCESS == rc ) { // // browse the value field and convert it into new SDDL format // the output is saved in the same buffer // pszValue[ValueSize/2] = L'\0'; rc = ScepConvertToSDDLFormat(pszValue, ValueSize/2); if ( SCESTATUS_SUCCESS == rc ) { rc = SceJetSetCurrentLine( hSection, pszValue, ValueSize ); } } LocalFree(pszValue); pszValue = NULL; } else { rc = SCESTATUS_NOT_ENOUGH_RESOURCE; } } if ( rc != SCESTATUS_SUCCESS ) { break; } // // read next line // rc = SceJetGetValue( hSection, SCEJET_NEXT_LINE, NULL, NULL, 0, NULL, NULL, 0, &ValueSize ); } if ( rc == SCESTATUS_RECORD_NOT_FOUND ) { rc = SCESTATUS_SUCCESS; } return rc; } SCESTATUS ScepMigrateDatabaseRevision1( IN PSCECONTEXT cxtProfile ) { if ( cxtProfile == NULL ) { return SCESTATUS_INVALID_PARAMETER; } JET_ERR JetErr; SCESTATUS rc; JET_TABLEID TableID; JET_COLUMNDEF ColumnDef; JET_COLUMNID ColumnID; rc = SceJetStartTransaction( cxtProfile ); if ( SCESTATUS_SUCCESS != rc ) { return(rc); } // // add "LastUsedMergeTable field to the version table // rc = SceJetOpenTable( cxtProfile, "SmTblVersion", SCEJET_TABLE_VERSION, SCEJET_OPEN_READ_WRITE, &TableID ); if ( SCESTATUS_SUCCESS == rc ) { ColumnDef.cbStruct = sizeof(JET_COLUMNDEF); ColumnDef.columnid = 0; ColumnDef.coltyp = JET_coltypLong; ColumnDef.wCountry = 0; ColumnDef.langid = 0; ColumnDef.cp = 0; ColumnDef.wCollate = 0; ColumnDef.cbMax = 4; ColumnDef.grbit = 0; JetErr = JetAddColumn( cxtProfile->JetSessionID, TableID, "LastUsedMergeTable", &ColumnDef, NULL, 0, &ColumnID ); if ( JET_errColumnDuplicate == JetErr ) { // // column already exist // JetErr = JET_errSuccess; } rc = SceJetJetErrorToSceStatus(JetErr); JetCloseTable( cxtProfile->JetSessionID, TableID ); } // // add "GpoID" filed to the SCP tables // if ( SCESTATUS_SUCCESS == rc ) { TableID = JET_tableidNil; rc = SceJetOpenTable( cxtProfile, "SmTblScp", SCEJET_TABLE_SCP, SCEJET_OPEN_READ_WRITE, &TableID ); if ( SCESTATUS_SUCCESS == rc ) { ColumnDef.cbStruct = sizeof(JET_COLUMNDEF); ColumnDef.columnid = 0; ColumnDef.coltyp = JET_coltypLong; ColumnDef.wCountry = 0; ColumnDef.langid = 0; ColumnDef.cp = 0; ColumnDef.wCollate = 0; ColumnDef.cbMax = 4; ColumnDef.grbit = 0; JetErr = JetAddColumn( cxtProfile->JetSessionID, TableID, "GpoID", &ColumnDef, NULL, 0, &ColumnID ); if ( JET_errColumnDuplicate == JetErr ) { // // column already exist // JetErr = JET_errSuccess; } rc = SceJetJetErrorToSceStatus(JetErr); JetCloseTable( cxtProfile->JetSessionID, TableID ); } } if ( SCESTATUS_SUCCESS == rc ) { // // create the second SCP table // rc = SceJetCreateTable( cxtProfile, "SmTblScp2", SCEJET_TABLE_SCP, SCEJET_CREATE_NO_TABLEID, NULL, NULL ); if ( rc == SCESTATUS_SUCCESS ) { // // create the GPO table // rc = SceJetCreateTable( cxtProfile, "SmTblGpo", SCEJET_TABLE_GPO, SCEJET_CREATE_NO_TABLEID, NULL, NULL ); } } if ( SCESTATUS_SUCCESS == rc ) { // // set new version # // FLOAT Version = (FLOAT)1.2; rc = SceJetSetValueInVersion( cxtProfile, "SmTblVersion", "Version", (PWSTR)(&Version), 4, // number of bytes JET_prepReplace ); } if ( SCESTATUS_SUCCESS == rc ) { SceJetCommitTransaction( cxtProfile, 0 ); } else { SceJetRollback( cxtProfile, 0 ); } return(rc); } SCESTATUS ScepDeleteOldRegValuesFromTable( IN PSCECONTEXT hProfile, IN SCETYPE TableType ) { PSCESECTION h_Section=NULL; if ( ScepOpenSectionForName( hProfile, TableType, szRegistryValues, &h_Section ) == SCESTATUS_SUCCESS ) { SceJetDelete( h_Section, TEXT("machine\\software\\microsoft\\windows nt\\currentversion\\winlogon\\disablecad"), FALSE, SCEJET_DELETE_LINE ); SceJetDelete( h_Section, TEXT("machine\\software\\microsoft\\windows nt\\currentversion\\winlogon\\dontdisplaylastusername"), FALSE, SCEJET_DELETE_LINE ); SceJetDelete( h_Section, TEXT("machine\\software\\microsoft\\windows nt\\currentversion\\winlogon\\legalnoticecaption"), FALSE, SCEJET_DELETE_LINE ); SceJetDelete( h_Section, TEXT("machine\\software\\microsoft\\windows nt\\currentversion\\winlogon\\legalnoticetext"), FALSE, SCEJET_DELETE_LINE ); SceJetDelete( h_Section, TEXT("machine\\software\\microsoft\\windows nt\\currentversion\\winlogon\\shutdownwithoutlogon"), FALSE, SCEJET_DELETE_LINE ); SceJetDelete( h_Section, TEXT("machine\\system\\currentcontrolset\\control\\lsa\\fullprivilegeauditing"), FALSE, SCEJET_DELETE_LINE ); SceJetCloseSection( &h_Section, TRUE ); } return(SCESTATUS_SUCCESS); } BOOL ScepCompareGroupNameList( IN PUNICODE_STRING DomainName, IN PSCE_NAME_LIST pListToCmp, IN PSCE_NAME_LIST pList ) { if ( DomainName == NULL ) { return(SceCompareNameList(pListToCmp, pList)); } PSCE_NAME_LIST pTmpList, pTL2; DWORD count1, count2; PWSTR pTemp1, pTemp2; for ( pTmpList=pListToCmp, count2=0; pTmpList!=NULL; pTmpList=pTmpList->Next) { if ( pTmpList->Name != NULL ) { count2++; } } for ( pTmpList=pList,count1=0; pTmpList!=NULL; pTmpList=pTmpList->Next) { if ( pTmpList->Name == NULL ) { continue; } count1++; pTemp1 = wcschr(pTmpList->Name, L'\\'); for ( pTL2=pListToCmp; pTL2 != NULL; pTL2=pTL2->Next ) { if ( pTL2->Name == NULL ) { continue; } pTemp2 = wcschr(pTmpList->Name, L'\\'); if ( ( pTemp1 == NULL && pTemp2 == NULL ) || ( pTemp1 != NULL && pTemp2 != NULL ) ) { if ( _wcsicmp( pTL2->Name, pTmpList->Name) == 0 ) { break; } } else if ( pTemp1 == NULL ) { if ( _wcsicmp( pTmpList->Name, pTemp2+1) == 0 ) { // // check if pTL2->Name's domain name equal to the account name // if ( pTemp2-pTL2->Name == DomainName->Length/2 && _wcsnicmp( pTL2->Name, DomainName->Buffer, DomainName->Length/2) == 0 ) { break; } } } else { // // if there is domain prefix, it must be acocunt domain // because this list is built by querying system // if ( _wcsicmp(pTemp1+1, pTL2->Name) == 0 ) { break; } } } if ( pTL2 == NULL ) { return(FALSE); } } if ( count1 == count2 ) { return(TRUE); } else { return(FALSE); } } SCESTATUS ScepGetSystemSecurity( IN AREA_INFORMATION Area, IN DWORD Options, OUT PSCE_PROFILE_INFO *ppInfo, OUT PSCE_ERROR_LOG_INFO *pErrLog ) // // query system settings for system access and user rights area only // { SCESTATUS Saverc = SCESTATUS_SUCCESS; SCESTATUS rc; if ( ppInfo == NULL || Area == 0 ) { // // nothing to set // return(Saverc); } // // initialize product type etc. // if ( RtlGetNtProductType (&ProductType) == FALSE ) { return( ScepDosErrorToSceStatus(GetLastError())); } // // allocate memory // *ppInfo = (PSCE_PROFILE_INFO)ScepAlloc( (UINT)0, sizeof(SCE_PROFILE_INFO)); if ( *ppInfo == NULL ) { return( SCESTATUS_NOT_ENOUGH_RESOURCE ); } // // reset local policy // memset(*ppInfo, '\0', sizeof(SCE_PROFILE_INFO)); ScepResetSecurityPolicyArea(*ppInfo); (*ppInfo)->Type = SCE_ENGINE_SMP; if ( Area & AREA_PRIVILEGES ) { rc = ScepGetSystemPrivileges( Options, pErrLog, &((*ppInfo)->OtherInfo.smp.pPrivilegeAssignedTo) ); if( rc != SCESTATUS_SUCCESS ) { Saverc = rc; } } if ( Area & AREA_SECURITY_POLICY ) { // // snapshot system access. Errors are logged within this function // rc = ScepAnalyzeSystemAccess(*ppInfo, NULL, SCEPOL_SYSTEM_SETTINGS, NULL, pErrLog); if( rc != SCESTATUS_SUCCESS ) { Saverc = rc; } // // Audit policy // PPOLICY_AUDIT_EVENTS_INFO AuditEvent=NULL; rc = ScepSaveAndOffAuditing(&AuditEvent, FALSE, NULL); if ( rc == SCESTATUS_SUCCESS && AuditEvent ) { // // assign auditEvent buffer to the output buffer // if ( !AuditEvent->AuditingMode ) { for ( DWORD i=0; iMaximumAuditEventCount; i++ ) AuditEvent->EventAuditingOptions[i] = 0; } (*ppInfo)->AuditSystemEvents = AuditEvent->EventAuditingOptions[AuditCategorySystem]; (*ppInfo)->AuditLogonEvents = AuditEvent->EventAuditingOptions[AuditCategoryLogon]; (*ppInfo)->AuditObjectAccess = AuditEvent->EventAuditingOptions[AuditCategoryObjectAccess]; (*ppInfo)->AuditPrivilegeUse = AuditEvent->EventAuditingOptions[AuditCategoryPrivilegeUse]; (*ppInfo)->AuditProcessTracking = AuditEvent->EventAuditingOptions[AuditCategoryDetailedTracking]; (*ppInfo)->AuditPolicyChange = AuditEvent->EventAuditingOptions[AuditCategoryPolicyChange]; (*ppInfo)->AuditAccountManage = AuditEvent->EventAuditingOptions[AuditCategoryAccountManagement]; (*ppInfo)->AuditDSAccess = AuditEvent->EventAuditingOptions[AuditCategoryDirectoryServiceAccess]; (*ppInfo)->AuditAccountLogon = AuditEvent->EventAuditingOptions[AuditCategoryAccountLogon]; } else { ScepBuildErrorLogInfo( ScepSceStatusToDosError(rc), pErrLog, SCEDLL_ERROR_QUERY_EVENT_AUDITING ); } if ( AuditEvent ) { LsaFreeMemory(AuditEvent); } if( rc != SCESTATUS_SUCCESS ) { Saverc = rc; } // // Kerberos Policy // (*ppInfo)->pKerberosInfo = (PSCE_KERBEROS_TICKET_INFO)ScepAlloc(LPTR,sizeof(SCE_KERBEROS_TICKET_INFO)); if ( (*ppInfo)->pKerberosInfo ) { (*ppInfo)->pKerberosInfo->MaxTicketAge = SCE_NO_VALUE; (*ppInfo)->pKerberosInfo->MaxRenewAge = SCE_NO_VALUE; (*ppInfo)->pKerberosInfo->MaxServiceAge = SCE_NO_VALUE; (*ppInfo)->pKerberosInfo->MaxClockSkew = SCE_NO_VALUE; (*ppInfo)->pKerberosInfo->TicketValidateClient = SCE_NO_VALUE; rc = ScepAnalyzeKerberosPolicy(NULL, (*ppInfo)->pKerberosInfo, Options | SCE_SYSTEM_SETTINGS ); } else { rc = SCESTATUS_NOT_ENOUGH_RESOURCE; } if( rc != SCESTATUS_SUCCESS ) { ScepBuildErrorLogInfo( ScepSceStatusToDosError(rc), pErrLog, SCEDLL_SAP_ERROR_KERBEROS ); Saverc = rc; } // // registry values // rc = ScepAnalyzeRegistryValues(NULL, SCEREG_VALUE_SYSTEM, *ppInfo ); if( rc != SCESTATUS_SUCCESS ) { ScepBuildErrorLogInfo( ScepSceStatusToDosError(rc), pErrLog, SCEDLL_ERROR_QUERY_INFO, szRegistryValues ); Saverc = rc; } } // // The buffer is not freed even if there is error. // because we want to show as many settings as we can. // return(Saverc); } SCESTATUS ScepGetSystemPrivileges( IN DWORD Options, IN OUT PSCE_ERROR_LOG_INFO *pErrLog, OUT PSCE_PRIVILEGE_ASSIGNMENT *pCurrent ) /* query privilege/user right assignments from the current system */ { if ( pCurrent == NULL ) { return(SCESTATUS_INVALID_PARAMETER); } NTSTATUS NtStatus; LSA_HANDLE PolicyHandle=NULL; DWORD rc; SCESTATUS saveRc=SCESTATUS_SUCCESS; ULONG CountReturned; UNICODE_STRING UserRight; PLSA_ENUMERATION_INFORMATION EnumBuffer=NULL; DWORD i=0, j; PLSA_TRANSLATED_NAME Names=NULL; PLSA_REFERENCED_DOMAIN_LIST ReferencedDomains=NULL; PSCE_NAME_LIST pNameList=NULL; PSCE_PRIVILEGE_ASSIGNMENT pPrivilege=NULL; // // Open LSA policy // NtStatus = ScepOpenLsaPolicy( POLICY_VIEW_LOCAL_INFORMATION | POLICY_LOOKUP_NAMES, &PolicyHandle, TRUE ); if ( !NT_SUCCESS(NtStatus) ) { rc = RtlNtStatusToDosError(NtStatus); if ( pErrLog ) { ScepBuildErrorLogInfo( rc, pErrLog, SCEDLL_LSA_POLICY ); } return(ScepDosErrorToSceStatus( rc )); } // // enumerate accounts for each user right // ScepIsDomainLocal(NULL); for ( i=0; iName = (PWSTR)ScepAlloc( (UINT)0, UserRight.Length+2); if ( pPrivilege->Name == NULL ) { ScepFree(pPrivilege); saveRc = SCESTATUS_NOT_ENOUGH_RESOURCE; break; } wcscpy(pPrivilege->Name, UserRight.Buffer); pPrivilege->Value = i; pPrivilege->Status = 0; // link it to the output buffer pPrivilege->Next = *pCurrent; *pCurrent = pPrivilege; if ( CountReturned > 0 ) { // // error occurred in lookup will be ignored // since accounts will be returned in SID format // NtStatus = LsaLookupSids( PolicyHandle, CountReturned, (PSID *)EnumBuffer, &ReferencedDomains, &Names ); rc = RtlNtStatusToDosError(rc); if ( NT_SUCCESS(NtStatus) ) { // // some may not be mapped // in this case, return the SID string // for ( j=0; jEntries > 0 && Names[j].Use != SidTypeWellKnownGroup && ReferencedDomains->Domains != NULL && Names[j].DomainIndex != -1 && (ULONG)(Names[j].DomainIndex) < ReferencedDomains->Entries && ScepIsDomainLocalBySid(ReferencedDomains->Domains[Names[j].DomainIndex].Sid) == FALSE && ScepIsDomainLocal(&ReferencedDomains->Domains[Names[j].DomainIndex].Name) == FALSE ) { // // add both domain name and account name // rc = ScepAddTwoNamesToNameList( &pNameList, TRUE, ReferencedDomains->Domains[Names[j].DomainIndex].Name.Buffer, ReferencedDomains->Domains[Names[j].DomainIndex].Name.Length/2, Names[j].Name.Buffer, Names[j].Name.Length/2); } else { // // add only the account name // rc = ScepAddToNameList( &pNameList, Names[j].Name.Buffer, Names[j].Name.Length/2); } } if ( NO_ERROR != rc ) { pPrivilege->Status = SCE_STATUS_ERROR_NOT_AVAILABLE; saveRc = ScepDosErrorToSceStatus(rc); } } } else { // // lookup for all sids failed or none is mapped // add the SIDs // for ( j=0; jStatus = SCE_STATUS_ERROR_NOT_AVAILABLE; saveRc = ScepDosErrorToSceStatus(rc); } } } if (ReferencedDomains) { LsaFreeMemory(ReferencedDomains); ReferencedDomains = NULL; } if (Names) { LsaFreeMemory(Names); Names = NULL; } if ( pPrivilege->Status ) { ScepBuildErrorLogInfo( rc, pErrLog, SCEDLL_SAP_ERROR_ENUMERATE, SCE_Privileges[i].Name ); } pPrivilege->AssignedTo = pNameList; pNameList = NULL; } LsaFreeMemory( EnumBuffer ); EnumBuffer = NULL; } else { // // no account is assigned this privilege // or the privilege is not found // should continue the process // } } if ( saveRc == SCESTATUS_NOT_ENOUGH_RESOURCE ) { // // should free the output buffer since there is no more memory // ScepFreePrivilege(*pCurrent); *pCurrent = NULL; } if ( pNameList != NULL ) ScepFreeNameList( pNameList ); LsaClose(PolicyHandle); return( saveRc ); } DWORD ScepAddSidStringToNameList( IN OUT PSCE_NAME_LIST *ppNameList, IN PSID pSid ) { NTSTATUS NtStatus; UNICODE_STRING UnicodeStringSid; DWORD rc; NtStatus = RtlConvertSidToUnicodeString( &UnicodeStringSid, pSid, TRUE ); rc = RtlNtStatusToDosError(NtStatus); if ( NT_SUCCESS( NtStatus ) ) { rc = ScepAddTwoNamesToNameList( ppNameList, FALSE, L"*", 1, UnicodeStringSid.Buffer, UnicodeStringSid.Length/2 ); RtlFreeUnicodeString( &UnicodeStringSid ); } return(rc); } DWORD ScepGetLSAPolicyObjectInfo( OUT DWORD *pdwAllow ) /* Routine Description: This routine checks whether the Anonymous User/SID has permissions to do SID<->name translation. No errors are logged by this routine - they will be logged outside if need be. Arguments: pdwAllow - pointer to the DWORD that is 1 if Anonymous User/SID has this permission, else 0 Return Value: Win32 error code */ { NTSTATUS NtStatus = STATUS_SUCCESS; DWORD rc = ERROR_SUCCESS; PACL pNewAcl = NULL; DWORD dwAceType; BOOL bAddAce = FALSE; PSECURITY_DESCRIPTOR pSDlsaPolicyObject = NULL; SECURITY_DESCRIPTOR SDAbsolute; if ( pdwAllow == NULL ) { return ERROR_INVALID_PARAMETER; } LSA_HANDLE LsaHandle = NULL; NtStatus = ScepOpenLsaPolicy( MAXIMUM_ALLOWED, &LsaHandle, TRUE ); rc = RtlNtStatusToDosError( NtStatus ); if ( rc == ERROR_SUCCESS ) { NtStatus = LsaQuerySecurityObject( LsaHandle, OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, &pSDlsaPolicyObject ); rc = RtlNtStatusToDosError( NtStatus ); if ( rc == ERROR_SUCCESS ) { if ( ghAuthzResourceManager ) { SID AnonymousSid; SID_IDENTIFIER_AUTHORITY NtAuth = SECURITY_NT_AUTHORITY; AUTHZ_CLIENT_CONTEXT_HANDLE hAuthzClientContext = NULL; LUID Identifier = {0}; RtlInitializeSid ( &AnonymousSid, &NtAuth, 1); *RtlSubAuthoritySid ( &AnonymousSid, 0 ) = SECURITY_ANONYMOUS_LOGON_RID; if ( AuthzInitializeContextFromSid(0, &AnonymousSid, ghAuthzResourceManager, 0, Identifier, NULL, &hAuthzClientContext) ) { AUTHZ_ACCESS_REPLY AuthzReply; AUTHZ_ACCESS_REQUEST AuthzRequest; ACCESS_MASK GrantedAccessMask; DWORD AuthzError; AuthzReply.ResultListLength = 1; AuthzReply.GrantedAccessMask = &GrantedAccessMask; AuthzReply.Error = &AuthzError; AuthzReply.SaclEvaluationResults = NULL; memset(&AuthzRequest, 0, sizeof(AuthzRequest)); AuthzRequest.DesiredAccess = POLICY_LOOKUP_NAMES; if ( AuthzAccessCheck(0, hAuthzClientContext, &AuthzRequest, NULL, pSDlsaPolicyObject, NULL, NULL, &AuthzReply, NULL) ) { // // check if existing access is different from desired access // if so, add the appropriate ACE // if ( GrantedAccessMask & POLICY_LOOKUP_NAMES ) { //ASSERT(AuthzError == ERROR_SUCCESS); *pdwAllow = 1; } else { //ASSERT(AuthzError == ERROR_ACCESS_DENIED || AuthzError == ERROR_PRIVILEGE_NOT_HELD); *pdwAllow = 0; } } else { rc = GetLastError(); } AuthzFreeContext( hAuthzClientContext ); } else { rc = GetLastError(); } } else { rc = ERROR_RESOURCE_NOT_PRESENT; } LsaFreeMemory(pSDlsaPolicyObject); } LsaClose(LsaHandle); } return rc; }