You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1434 lines
36 KiB
1434 lines
36 KiB
/*++
|
|
|
|
Copyright (c) 1987-1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
polfiltr.cpp
|
|
|
|
Abstract:
|
|
|
|
Policy notification implementation to support down level API calls.
|
|
|
|
This file implements the notification logic when down level APIs
|
|
(LSA/SAM) are called by other apps to change security policies. The
|
|
policy changes are written to LSA database or DS, and also they are
|
|
required to be written to the policy storage on NT5 so policy
|
|
propagation won't overwrite the settings.
|
|
|
|
|
|
Environment:
|
|
|
|
User mode only.
|
|
Contains NT-specific code.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
//
|
|
// Common include files.
|
|
//
|
|
|
|
#include "headers.h"
|
|
#include "scerpc.h"
|
|
#include "scesetup.h"
|
|
#include "sceutil.h"
|
|
#include "clntutil.h"
|
|
#include "scedllrc.h"
|
|
#include <ntrpcp.h>
|
|
#include <ntsam.h>
|
|
#include <dsrole.h>
|
|
#include <sddl.h>
|
|
|
|
//#include <gpedit.h>
|
|
//#include <initguid.h>
|
|
|
|
//#include <winldap.h>
|
|
//#include <dsgetdc.h>
|
|
#include <ntdsapi.h>
|
|
#include <io.h>
|
|
//#include "infp.h"
|
|
#include <rpcasync.h>
|
|
|
|
#pragma hdrstop
|
|
|
|
extern HINSTANCE MyModuleHandle;
|
|
|
|
//typedef DWORD (WINAPI *PFNDSGETDCNAME)(LPCTSTR, LPCTSTR, GUID *, LPCTSTR, ULONG, PDOMAIN_CONTROLLER_INFO *);
|
|
|
|
typedef VOID (WINAPI *PFNDSROLEFREE)(PVOID);
|
|
|
|
typedef DWORD (WINAPI *PFNDSROLEGETINFO)(LPCWSTR,DSROLE_PRIMARY_DOMAIN_INFO_LEVEL,PBYTE *);
|
|
|
|
typedef struct _SCEP_NOTIFYARGS_NODE {
|
|
LIST_ENTRY List;
|
|
SECURITY_DB_TYPE DbType;
|
|
SECURITY_DB_DELTA_TYPE DeltaType;
|
|
SECURITY_DB_OBJECT_TYPE ObjectType;
|
|
PSID ObjectSid;
|
|
} SCEP_NOTIFYARGS_NODE, *PSCEP_NOTIFYARGS_NODE;
|
|
|
|
|
|
static DSROLE_MACHINE_ROLE MachineRole;
|
|
static BOOL bRoleQueried=FALSE;
|
|
static ULONG DsRoleFlags=0;
|
|
|
|
static PSID BuiltinDomainSid=NULL;
|
|
|
|
CRITICAL_SECTION PolicyNotificationSync;
|
|
static DWORD SceNotifyCount=0;
|
|
static BOOL gSceNotificationThreadActive=FALSE;
|
|
LIST_ENTRY ScepNotifyList;
|
|
|
|
DWORD
|
|
ScepNotifyWorkerThread(
|
|
PVOID Ignored
|
|
);
|
|
|
|
DWORD
|
|
ScepNotifySaveInPolicyStorage(
|
|
IN SECURITY_DB_TYPE DbType,
|
|
IN SECURITY_DB_DELTA_TYPE DeltaType,
|
|
IN SECURITY_DB_OBJECT_TYPE ObjectType,
|
|
IN PSID ObjectSid
|
|
);
|
|
|
|
DWORD
|
|
ScepNotifySaveChangeInServer(
|
|
IN SECURITY_DB_TYPE DbType,
|
|
IN SECURITY_DB_DELTA_TYPE DeltaType,
|
|
IN SECURITY_DB_OBJECT_TYPE ObjectType,
|
|
IN PSID ObjectSid OPTIONAL,
|
|
IN BOOL bDCGPO,
|
|
IN DWORD ExplicitLowRight,
|
|
IN DWORD ExplicitHighRight
|
|
);
|
|
|
|
DWORD
|
|
ScepNotifyFailureLog(
|
|
IN DWORD ErrCode,
|
|
IN UINT idMsg,
|
|
IN DWORD DbType,
|
|
IN DWORD ObjectType,
|
|
IN PWSTR Message
|
|
);
|
|
|
|
DWORD
|
|
ScepNotificationRequest(
|
|
PSCEP_NOTIFYARGS_NODE Node
|
|
);
|
|
|
|
DWORD
|
|
ScepSendNotificationNodeToServer(
|
|
PSCEP_NOTIFYARGS_NODE Node
|
|
);
|
|
|
|
NTSTATUS
|
|
WINAPI
|
|
SceNotifyPolicyDelta (
|
|
IN SECURITY_DB_TYPE DbType,
|
|
IN SECURITY_DB_DELTA_TYPE DeltaType,
|
|
IN SECURITY_DB_OBJECT_TYPE ObjectType,
|
|
IN PSID ObjectSid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called by the LSA after each change is
|
|
made to the LSA database (SAM calls DeltaNotify - not this funtion).
|
|
The LSA describes the type of object that is modified, the type of modification
|
|
made on the object, etc.
|
|
This information will be used to query the system settings and store in
|
|
the policy storage (GPO on DC, LPO on workstation/server).
|
|
|
|
Arguments:
|
|
|
|
DbType - Type of the database that has been modified.
|
|
|
|
DeltaType - The type of modification that has been made on the object.
|
|
|
|
ObjectType - The type of object that has been modified.
|
|
|
|
ObjectSid - The SID of the object that has been modified.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - The Service completed successfully.
|
|
|
|
--*/
|
|
{
|
|
DWORD dwPolicyFilterOff=0;
|
|
|
|
ScepRegQueryIntValue(
|
|
HKEY_LOCAL_MACHINE,
|
|
SCE_ROOT_PATH,
|
|
TEXT("PolicyFilterOff"),
|
|
&dwPolicyFilterOff
|
|
);
|
|
|
|
if ( dwPolicyFilterOff ) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if ( DbType == SecurityDbLsa ) {
|
|
//
|
|
// LSA policy changes
|
|
//
|
|
if ( ObjectType != SecurityDbObjectLsaPolicy &&
|
|
ObjectType != SecurityDbObjectLsaAccount ) {
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
} else if ( DbType == SecurityDbSam ) {
|
|
|
|
//
|
|
// SAM policy changes is supported by the standard
|
|
// SAM change notification mechanism
|
|
// this parameter here is not used (should not be
|
|
// called by LSA
|
|
//
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
//
|
|
// unknown database, do nothing.
|
|
//
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
//
|
|
// Map object type and delta type to NetlogonDeltaType
|
|
//
|
|
|
|
switch( ObjectType ) {
|
|
case SecurityDbObjectLsaPolicy:
|
|
|
|
switch (DeltaType) {
|
|
case SecurityDbNew:
|
|
case SecurityDbChange:
|
|
break;
|
|
|
|
// unknown delta type
|
|
default:
|
|
return STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
|
|
case SecurityDbObjectLsaAccount:
|
|
|
|
switch (DeltaType) {
|
|
case SecurityDbNew:
|
|
case SecurityDbChange:
|
|
case SecurityDbDelete:
|
|
break;
|
|
|
|
// unknown delta type
|
|
default:
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if ( ObjectSid == NULL ) {
|
|
// for privileges, must have a Sid
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
// unknown object type
|
|
// SAM policy is filtered in DeltaNotify routine
|
|
//
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Save the change to SCE policy storage
|
|
//
|
|
|
|
(VOID) ScepNotifySaveInPolicyStorage(DbType,
|
|
DeltaType,
|
|
ObjectType,
|
|
ObjectSid
|
|
);
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
DWORD
|
|
ScepNotifySaveInPolicyStorage(
|
|
IN SECURITY_DB_TYPE DbType,
|
|
IN SECURITY_DB_DELTA_TYPE DeltaType,
|
|
IN SECURITY_DB_OBJECT_TYPE ObjectType,
|
|
IN PSID ObjectSid
|
|
)
|
|
{
|
|
DWORD rc=ERROR_SUCCESS;
|
|
|
|
if ( !bRoleQueried ||
|
|
MachineRole == DsRole_RoleBackupDomainController ||
|
|
MachineRole == DsRole_RolePrimaryDomainController ) {
|
|
|
|
|
|
} else {
|
|
|
|
//
|
|
// no filter on non-DCs
|
|
//
|
|
return(rc);
|
|
|
|
}
|
|
|
|
//
|
|
// make a structure to pass into the new asynchronous thread
|
|
//
|
|
|
|
SCEP_NOTIFYARGS_NODE *pEA = (SCEP_NOTIFYARGS_NODE *)LocalAlloc(LPTR, sizeof(SCEP_NOTIFYARGS_NODE));
|
|
|
|
if ( pEA ) {
|
|
|
|
pEA->DbType = DbType;
|
|
pEA->DeltaType = DeltaType;
|
|
pEA->ObjectType = ObjectType;
|
|
|
|
if ( ObjectSid ) {
|
|
//
|
|
// need to make a new buffer for this SID because once it's returned
|
|
// it will be freed.
|
|
//
|
|
DWORD Len = RtlLengthSid(ObjectSid);
|
|
|
|
pEA->ObjectSid = (PSID)LocalAlloc(0, Len+1);
|
|
if ( pEA->ObjectSid ) {
|
|
|
|
RtlCopySid (
|
|
Len+1,
|
|
pEA->ObjectSid,
|
|
ObjectSid
|
|
);
|
|
} else {
|
|
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
LocalFree(pEA);
|
|
}
|
|
|
|
} else {
|
|
pEA->ObjectSid = NULL;
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
if ( ERROR_SUCCESS == rc ) {
|
|
|
|
//
|
|
// create another thread to call to engine
|
|
// (to make sure that the current LSA call is not blocked)
|
|
// because in engine, it quries the same change using LSA apis.
|
|
//
|
|
// note, when this is called, LSA is not impersonating
|
|
// so the current calling context is running under system
|
|
// context. No need (no way) to impersonate.
|
|
//
|
|
rc = ScepNotificationRequest(pEA);
|
|
|
|
if ( ERROR_SUCCESS != rc ) {
|
|
|
|
//
|
|
// error occurs to queue the work item, the memory won't
|
|
// be freed by the thread, so free it here
|
|
//
|
|
|
|
if ( pEA->ObjectSid ) {
|
|
LocalFree(pEA->ObjectSid);
|
|
}
|
|
LocalFree(pEA);
|
|
|
|
ScepNotifyFailureLog(rc,
|
|
IDS_ERROR_CREATE_THREAD,
|
|
(DWORD)DbType,
|
|
(DWORD)ObjectType,
|
|
NULL
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ScepNotifyFailureLog(rc,
|
|
IDS_ERROR_CREATE_THREAD_PARAM,
|
|
(DWORD)DbType,
|
|
(DWORD)ObjectType,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
DWORD
|
|
ScepNotificationRequest(
|
|
PSCEP_NOTIFYARGS_NODE Node
|
|
)
|
|
{
|
|
BOOL Ret = TRUE ;
|
|
DWORD rCode = ERROR_SUCCESS;
|
|
|
|
//
|
|
// Increment the notification count
|
|
//
|
|
EnterCriticalSection(&PolicyNotificationSync);
|
|
SceNotifyCount++;
|
|
|
|
if ( gSceNotificationThreadActive == FALSE )
|
|
{
|
|
Ret = QueueUserWorkItem( ScepNotifyWorkerThread, NULL, 0 );
|
|
}
|
|
|
|
if ( Ret )
|
|
{
|
|
InsertTailList( &ScepNotifyList, &Node->List );
|
|
|
|
// ScepNotifyFailureLog(0, 0, SceNotifyCount, 0, L"Add the request");
|
|
|
|
} else {
|
|
|
|
rCode = GetLastError();
|
|
//
|
|
// decrement the count
|
|
//
|
|
if ( SceNotifyCount > 0 ) SceNotifyCount--;
|
|
|
|
}
|
|
|
|
LeaveCriticalSection(&PolicyNotificationSync);
|
|
|
|
return rCode ;
|
|
}
|
|
|
|
|
|
DWORD
|
|
ScepNotifyFailureLog(
|
|
IN DWORD ErrCode,
|
|
IN UINT idMsg,
|
|
IN DWORD DbType,
|
|
IN DWORD ObjectType,
|
|
IN PWSTR Message
|
|
)
|
|
{
|
|
//
|
|
// build the log file name %windir%\security\logs\Notify.log
|
|
//
|
|
|
|
WCHAR LogName[MAX_PATH+51];
|
|
|
|
LogName[0] = L'\0';
|
|
GetSystemWindowsDirectory(LogName, MAX_PATH);
|
|
LogName[MAX_PATH] = L'\0';
|
|
|
|
wcscat(LogName, L"\\security\\logs\\notify.log\0");
|
|
|
|
HANDLE hFile = CreateFile(LogName,
|
|
GENERIC_WRITE,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if (hFile != INVALID_HANDLE_VALUE) {
|
|
|
|
DWORD dwBytesWritten;
|
|
|
|
SetFilePointer (hFile, 0, NULL, FILE_BEGIN);
|
|
|
|
CHAR TmpBuf[3];
|
|
TmpBuf[0] = (CHAR)0xFF;
|
|
TmpBuf[1] = (CHAR)0xFE;
|
|
TmpBuf[2] = '\0';
|
|
WriteFile (hFile, (LPCVOID)TmpBuf, 2,
|
|
&dwBytesWritten,
|
|
NULL);
|
|
|
|
SetFilePointer (hFile, 0, NULL, FILE_END);
|
|
|
|
//
|
|
// print a time stamp
|
|
//
|
|
LARGE_INTEGER CurrentTime;
|
|
LARGE_INTEGER SysTime;
|
|
TIME_FIELDS TimeFields;
|
|
NTSTATUS NtStatus;
|
|
|
|
NtStatus = NtQuerySystemTime(&SysTime);
|
|
|
|
RtlSystemTimeToLocalTime (&SysTime,&CurrentTime);
|
|
|
|
if ( NT_SUCCESS(NtStatus) &&
|
|
(CurrentTime.LowPart != 0 || CurrentTime.HighPart != 0) ) {
|
|
|
|
memset(&TimeFields, 0, sizeof(TIME_FIELDS));
|
|
|
|
RtlTimeToTimeFields (
|
|
&CurrentTime,
|
|
&TimeFields
|
|
);
|
|
if ( TimeFields.Month > 0 && TimeFields.Month <= 12 &&
|
|
TimeFields.Day > 0 && TimeFields.Day <= 31 &&
|
|
TimeFields.Year > 1600 ) {
|
|
|
|
ScepWriteVariableUnicodeLog(hFile, TRUE,
|
|
L"\r\n----------------%02d/%02d/%04d %02d:%02d:%02d",
|
|
TimeFields.Month,
|
|
TimeFields.Day,
|
|
TimeFields.Year,
|
|
TimeFields.Hour,
|
|
TimeFields.Minute,
|
|
TimeFields.Second);
|
|
} else {
|
|
ScepWriteVariableUnicodeLog(hFile, TRUE,
|
|
L"\r\n----------------%08x 08x",
|
|
CurrentTime.HighPart,
|
|
CurrentTime.LowPart);
|
|
}
|
|
} else {
|
|
ScepWriteSingleUnicodeLog(hFile, TRUE, L"\r\n----------------Unknown time");
|
|
}
|
|
|
|
//
|
|
// print operation status code
|
|
//
|
|
if ( ErrCode ) {
|
|
ScepWriteVariableUnicodeLog(hFile, FALSE,
|
|
L"Thread %x\tError=%d",
|
|
GetCurrentThreadId(),
|
|
ErrCode
|
|
);
|
|
|
|
} else {
|
|
ScepWriteVariableUnicodeLog(hFile, FALSE,
|
|
L"Thread %x\t",
|
|
GetCurrentThreadId()
|
|
);
|
|
}
|
|
|
|
//
|
|
// operation type
|
|
//
|
|
|
|
if (Message ) {
|
|
swprintf(LogName, L"\t%x\0",DbType);
|
|
ScepWriteSingleUnicodeLog(hFile, FALSE, LogName);
|
|
} else {
|
|
|
|
switch (DbType) {
|
|
case SecurityDbLsa:
|
|
ScepWriteSingleUnicodeLog(hFile, FALSE, L"\tLSA");
|
|
break;
|
|
case SecurityDbSam:
|
|
ScepWriteSingleUnicodeLog(hFile, FALSE, L"\tSAM");
|
|
break;
|
|
default:
|
|
ScepWriteSingleUnicodeLog(hFile, FALSE, L"\tUnknown");
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// print object type
|
|
//
|
|
|
|
if (Message ) {
|
|
swprintf(LogName, L"\t%x\0",ObjectType);
|
|
ScepWriteSingleUnicodeLog(hFile, FALSE, LogName);
|
|
} else {
|
|
switch (ObjectType) {
|
|
case SecurityDbObjectLsaPolicy:
|
|
ScepWriteSingleUnicodeLog(hFile, FALSE, L"\tPolicy");
|
|
break;
|
|
case SecurityDbObjectLsaAccount:
|
|
ScepWriteSingleUnicodeLog(hFile, FALSE, L"\tAccount");
|
|
break;
|
|
case SecurityDbObjectSamDomain:
|
|
ScepWriteSingleUnicodeLog(hFile, FALSE, L"\tDomain");
|
|
break;
|
|
case SecurityDbObjectSamUser:
|
|
case SecurityDbObjectSamGroup:
|
|
case SecurityDbObjectSamAlias:
|
|
ScepWriteSingleUnicodeLog(hFile, FALSE, L"\tAccount");
|
|
break;
|
|
default:
|
|
ScepWriteSingleUnicodeLog(hFile, FALSE, L"\tUnknown");
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// load message
|
|
// print the name(s)
|
|
//
|
|
|
|
ScepWriteSingleUnicodeLog(hFile, FALSE, L"\t");
|
|
|
|
if (idMsg != 0) {
|
|
LogName[0] = L'\0';
|
|
LoadString (MyModuleHandle, idMsg, LogName, MAX_PATH);
|
|
|
|
ScepWriteSingleUnicodeLog(hFile, TRUE, LogName);
|
|
|
|
} else if (Message ) {
|
|
|
|
ScepWriteSingleUnicodeLog(hFile, FALSE, Message);
|
|
}
|
|
|
|
CloseHandle (hFile);
|
|
|
|
} else {
|
|
return(GetLastError());
|
|
}
|
|
|
|
return(ERROR_SUCCESS);
|
|
}
|
|
|
|
|
|
DWORD
|
|
ScepNotifyWorkerThread(
|
|
PVOID Ignored
|
|
)
|
|
{
|
|
|
|
PSCEP_NOTIFYARGS_NODE Node;
|
|
PLIST_ENTRY List ;
|
|
DWORD rc=0;
|
|
|
|
EnterCriticalSection(&PolicyNotificationSync);
|
|
|
|
//
|
|
// if there is already a work thread on the notification, just return
|
|
//
|
|
if ( gSceNotificationThreadActive )
|
|
{
|
|
|
|
LeaveCriticalSection(&PolicyNotificationSync);
|
|
|
|
return 0 ;
|
|
}
|
|
|
|
//
|
|
// set the flag to be active
|
|
//
|
|
gSceNotificationThreadActive = TRUE ;
|
|
|
|
// count is incremented in the main thread when the item is queued.
|
|
// it may be before or after this thread.
|
|
// InitializeEvents will check if the event is already initialized
|
|
|
|
(void) InitializeEvents(L"SceCli");
|
|
|
|
while ( !IsListEmpty( &ScepNotifyList ) )
|
|
{
|
|
List = RemoveHeadList( &ScepNotifyList );
|
|
|
|
LeaveCriticalSection(&PolicyNotificationSync);
|
|
|
|
//
|
|
// get the node
|
|
//
|
|
Node = CONTAINING_RECORD( List, SCEP_NOTIFYARGS_NODE, List );
|
|
|
|
rc = ScepSendNotificationNodeToServer( Node );
|
|
|
|
EnterCriticalSection(&PolicyNotificationSync);
|
|
|
|
//
|
|
// decrement the global count
|
|
//
|
|
|
|
if ( SceNotifyCount > 0 )
|
|
SceNotifyCount--;
|
|
|
|
// ScepNotifyFailureLog(0, 0, SceNotifyCount, rc, L"Send over to server");
|
|
}
|
|
|
|
gSceNotificationThreadActive = FALSE ;
|
|
|
|
//
|
|
// only shutdown events when there is no pending notification
|
|
//
|
|
if ( SceNotifyCount == 0 )
|
|
(void) ShutdownEvents();
|
|
|
|
LeaveCriticalSection(&PolicyNotificationSync);
|
|
|
|
return 0 ;
|
|
}
|
|
|
|
DWORD
|
|
ScepSendNotificationNodeToServer(
|
|
PSCEP_NOTIFYARGS_NODE Node
|
|
)
|
|
{
|
|
DWORD rc=ERROR_SUCCESS;
|
|
|
|
//
|
|
// get machine role. If it's a DC, policy is saved to
|
|
// the group policy object; if it's a server or workstation
|
|
// policy is saved into the local SCE database.
|
|
//
|
|
|
|
if ( !bRoleQueried ) {
|
|
|
|
rc = ScepGetDomainRoleInfo(&MachineRole, &DsRoleFlags, NULL);
|
|
|
|
if( ERROR_SUCCESS == rc)
|
|
{
|
|
bRoleQueried = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// we don't check for error because we
|
|
// default to DsRole_RoleStandaloneWorkstation
|
|
//
|
|
|
|
if (Node->DbType == SecurityDbSam &&
|
|
Node->ObjectType == SecurityDbObjectSamDomain &&
|
|
Node->DeltaType != SecurityDbDelete &&
|
|
DsRole_RoleBackupDomainController == MachineRole) {
|
|
|
|
// ANZ hotfix
|
|
|
|
//
|
|
// do not filter SAM domain policies on BDCs, except for delete notifications
|
|
//
|
|
DbgPrint("\n Dropping SAM notification on BDCs\n");
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
//
|
|
// when policy propagates, SAM notifications should be blocked
|
|
// because policy might change SAM policies.
|
|
//
|
|
|
|
//
|
|
// if event is not openable, allow the notification to go through
|
|
// (since at reboot, downlevel APIs may trigger SAM notifications before
|
|
// SCE server has started where the event is created)
|
|
//
|
|
|
|
|
|
if (rc != ERROR_SUCCESS ) {
|
|
|
|
//
|
|
// This isn't supposed to happen.
|
|
// if it really happens, assuming it's a workstation/server
|
|
//
|
|
|
|
MachineRole = DsRole_RoleStandaloneWorkstation;
|
|
|
|
LogEvent(MyModuleHandle,
|
|
STATUS_SEVERITY_WARNING,
|
|
SCEEVENT_WARNING_MACHINE_ROLE,
|
|
IDS_ERROR_GET_ROLE,
|
|
rc
|
|
);
|
|
}
|
|
|
|
//
|
|
// policy filter shouldn't run on non-DCs.
|
|
//
|
|
if ( MachineRole == DsRole_RoleBackupDomainController ||
|
|
MachineRole == DsRole_RolePrimaryDomainController ) {
|
|
|
|
//
|
|
// if dcpromo upgrade in progress, any account policy
|
|
// change should be ignored (because the SAM hive is temperatory)
|
|
// any privilege change for account domain accounts (not well
|
|
// known, and not builtin) should also be ignored.
|
|
//
|
|
if ( !(DsRoleFlags & DSROLE_UPGRADE_IN_PROGRESS) ||
|
|
( ( Node->DbType != SecurityDbSam ) &&
|
|
( ( Node->ObjectType != SecurityDbObjectLsaAccount ) ||
|
|
!ScepIsSidFromAccountDomain( Node->ObjectSid ) ) ) ) {
|
|
|
|
//
|
|
// ignore any policy changes within dcpromo upgrade
|
|
//
|
|
// domain controllers, write to the default GPOs
|
|
//
|
|
|
|
rc = ScepNotifySaveChangeInServer(
|
|
Node->DbType,
|
|
Node->DeltaType,
|
|
Node->ObjectType,
|
|
Node->ObjectSid,
|
|
TRUE,
|
|
0,
|
|
0
|
|
);
|
|
|
|
if ( ERROR_SUCCESS != rc &&
|
|
RPC_S_SERVER_UNAVAILABLE != rc ) {
|
|
|
|
LogEvent(MyModuleHandle,
|
|
STATUS_SEVERITY_ERROR,
|
|
SCEEVENT_ERROR_POLICY_QUEUE,
|
|
IDS_ERROR_SAVE_POLICY_GPO,
|
|
rc
|
|
);
|
|
}
|
|
}
|
|
|
|
} // turn off policy filter for non-DCs
|
|
|
|
Exit:
|
|
|
|
if ( Node->ObjectSid ) {
|
|
LocalFree(Node->ObjectSid);
|
|
}
|
|
|
|
LocalFree(Node);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
DWORD
|
|
ScepNotifySaveChangeInServer(
|
|
IN SECURITY_DB_TYPE DbType,
|
|
IN SECURITY_DB_DELTA_TYPE DeltaType,
|
|
IN SECURITY_DB_OBJECT_TYPE ObjectType,
|
|
IN PSID ObjectSid OPTIONAL,
|
|
IN BOOL bDCGPO,
|
|
IN DWORD ExplicitLowRight,
|
|
IN DWORD ExplicitHighRight
|
|
)
|
|
{
|
|
|
|
//
|
|
// call RPC interface to the server where query and set the changes
|
|
// to the template or to the database
|
|
//
|
|
|
|
handle_t binding_h;
|
|
NTSTATUS NtStatus;
|
|
DWORD rc;
|
|
|
|
//
|
|
// RPC bind to the server (secure is not required)
|
|
//
|
|
|
|
NtStatus = ScepBindRpc(
|
|
NULL,
|
|
L"scerpc",
|
|
0,
|
|
&binding_h
|
|
);
|
|
|
|
rc = RtlNtStatusToDosError(NtStatus);
|
|
|
|
if (NT_SUCCESS(NtStatus)){
|
|
|
|
RpcTryExcept {
|
|
|
|
//
|
|
// send the changes to server side to determine
|
|
// if and where to save it
|
|
//
|
|
|
|
if ( bDCGPO ) {
|
|
|
|
rc = SceRpcNotifySaveChangesInGP(
|
|
binding_h,
|
|
(DWORD)DbType,
|
|
(DWORD)DeltaType,
|
|
(DWORD)ObjectType,
|
|
(PSCEPR_SID)ObjectSid,
|
|
ExplicitLowRight,
|
|
ExplicitHighRight
|
|
);
|
|
} // else do not filter
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
//
|
|
// get exception code (DWORD)
|
|
//
|
|
|
|
rc = RpcExceptionCode();
|
|
|
|
} RpcEndExcept;
|
|
|
|
//
|
|
// Free the binding handle
|
|
//
|
|
|
|
RpcpUnbindRpc( binding_h );
|
|
|
|
}
|
|
|
|
return(rc);
|
|
|
|
}
|
|
|
|
DWORD
|
|
ScepProcessPolicyFilterTempFiles(
|
|
IN LPTSTR LogFileName OPTIONAL
|
|
)
|
|
{
|
|
|
|
DWORD dwpPolicy=0;
|
|
|
|
ScepRegQueryIntValue(
|
|
HKEY_LOCAL_MACHINE,
|
|
SCE_ROOT_PATH,
|
|
TEXT("PolicyChangedInSetup"),
|
|
(DWORD *)&dwpPolicy
|
|
);
|
|
|
|
if ( dwpPolicy ) {
|
|
|
|
LogEventAndReport(MyModuleHandle,
|
|
LogFileName,
|
|
0,
|
|
0,
|
|
IDS_FILTER_AFTER_SETUP,
|
|
L""
|
|
);
|
|
//
|
|
// this is the reboot after setup, no need to detect if this is a DC
|
|
// because the above reg value shouldn't be set for other product types
|
|
//
|
|
|
|
//
|
|
// build the temp file name
|
|
//
|
|
TCHAR TmpFileName[MAX_PATH+50];
|
|
|
|
memset(TmpFileName, '\0', (MAX_PATH+50)*sizeof(TCHAR));
|
|
|
|
GetSystemWindowsDirectory(TmpFileName, MAX_PATH);
|
|
lstrcat(TmpFileName, TEXT("\\security\\filtemp.inf"));
|
|
|
|
INT iNotify = GetPrivateProfileInt( L"Policies",
|
|
L"LsaPolicy",
|
|
0,
|
|
TmpFileName
|
|
);
|
|
if ( iNotify == 1 ) {
|
|
//
|
|
// Lsa policy is changed in setup
|
|
//
|
|
|
|
LogEventAndReport(MyModuleHandle,
|
|
LogFileName,
|
|
0,
|
|
0,
|
|
IDS_LSA_CHANGED_IN_SETUP,
|
|
L""
|
|
);
|
|
|
|
ScepNotifySaveChangeInServer(
|
|
SecurityDbLsa,
|
|
SecurityDbChange,
|
|
SecurityDbObjectLsaPolicy,
|
|
NULL,
|
|
TRUE,
|
|
0,
|
|
0
|
|
);
|
|
}
|
|
|
|
iNotify = GetPrivateProfileInt( L"Policies",
|
|
L"SamPolicy",
|
|
0,
|
|
TmpFileName
|
|
);
|
|
if ( iNotify == 1 ) {
|
|
|
|
//
|
|
// SAM policy is changed in setup
|
|
//
|
|
LogEventAndReport(MyModuleHandle,
|
|
LogFileName,
|
|
0,
|
|
0,
|
|
IDS_SAM_CHANGED_IN_SETUP,
|
|
L""
|
|
);
|
|
|
|
ScepNotifySaveChangeInServer(
|
|
SecurityDbSam,
|
|
SecurityDbChange,
|
|
SecurityDbObjectSamDomain,
|
|
NULL,
|
|
TRUE,
|
|
0,
|
|
0
|
|
);
|
|
}
|
|
|
|
//
|
|
// process all the accounts for user right changes
|
|
//
|
|
|
|
DWORD nSize;
|
|
DWORD rLen=0;
|
|
PWSTR SidBuffer = NULL;
|
|
|
|
iNotify = 1;
|
|
|
|
do {
|
|
|
|
if ( SidBuffer ) {
|
|
LocalFree(SidBuffer);
|
|
}
|
|
|
|
iNotify++;
|
|
nSize = MAX_PATH*iNotify;
|
|
|
|
SidBuffer = (PWSTR)LocalAlloc(0, nSize*sizeof(TCHAR));
|
|
|
|
if ( SidBuffer ) {
|
|
|
|
SidBuffer[0] = L'\0';
|
|
SidBuffer[1] = L'\0';
|
|
|
|
rLen = GetPrivateProfileSection(
|
|
L"Accounts",
|
|
SidBuffer,
|
|
nSize,
|
|
TmpFileName
|
|
);
|
|
|
|
} else {
|
|
|
|
rLen = 0;
|
|
}
|
|
|
|
} while ( rLen == nSize - 2 );
|
|
|
|
|
|
//
|
|
// find accounts, search for the '=' sign
|
|
//
|
|
PWSTR pStart = SidBuffer;
|
|
PWSTR pTemp, pTemp2;
|
|
PSID ObjectSid=NULL;
|
|
|
|
while ( pStart && pStart[0] != L'\0' ) {
|
|
|
|
pTemp = wcschr(pStart, L'=');
|
|
|
|
if ( pTemp ) {
|
|
*pTemp = L'\0';
|
|
|
|
LogEventAndReport(MyModuleHandle,
|
|
LogFileName,
|
|
0,
|
|
0,
|
|
0,
|
|
pStart
|
|
);
|
|
|
|
if ( ConvertStringSidToSid(
|
|
pStart,
|
|
&ObjectSid
|
|
) ) {
|
|
|
|
nSize = pTemp[1] - L'0';
|
|
rLen = _wtol(pTemp+3);
|
|
|
|
DWORD dwHigh=0;
|
|
|
|
// search for the high value
|
|
pTemp2 = wcschr(pTemp+3, L' ');
|
|
|
|
if ( pTemp2 ) {
|
|
|
|
dwHigh = _wtol(pTemp2+1);
|
|
|
|
}
|
|
|
|
LogEventAndReport(MyModuleHandle,
|
|
LogFileName,
|
|
0,
|
|
0,
|
|
IDS_FILTER_NOTIFY_SERVER,
|
|
L""
|
|
);
|
|
|
|
ScepNotifySaveChangeInServer(
|
|
SecurityDbLsa,
|
|
(SECURITY_DB_DELTA_TYPE)nSize,
|
|
SecurityDbObjectLsaAccount,
|
|
ObjectSid,
|
|
TRUE,
|
|
rLen,
|
|
dwHigh
|
|
);
|
|
|
|
}
|
|
|
|
if ( ObjectSid ) {
|
|
LocalFree(ObjectSid);
|
|
ObjectSid = NULL;
|
|
}
|
|
|
|
*pTemp = L'=';
|
|
}
|
|
|
|
pTemp = pStart + wcslen(pStart) + 1;
|
|
pStart = pTemp;
|
|
}
|
|
|
|
if ( SidBuffer ) {
|
|
LocalFree(SidBuffer);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// delete the key and the temp file
|
|
// for debugging purpose, leave the file
|
|
//
|
|
|
|
ScepClearPolicyFilterTempFiles(TRUE);
|
|
// ScepClearPolicyFilterTempFiles(FALSE);
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
DWORD
|
|
ScepClearPolicyFilterTempFiles(
|
|
BOOL bClearFile
|
|
)
|
|
{
|
|
|
|
if ( bClearFile ) {
|
|
|
|
TCHAR Buffer[MAX_PATH+51];
|
|
|
|
Buffer[0] = L'\0';
|
|
GetSystemWindowsDirectory(Buffer, MAX_PATH);
|
|
Buffer[MAX_PATH] = L'\0';
|
|
|
|
wcscat(Buffer, L"\\security\\filtemp.inf\0");
|
|
|
|
DeleteFile(Buffer);
|
|
}
|
|
|
|
//
|
|
// delete the registry value
|
|
//
|
|
|
|
DWORD rc = ScepRegDeleteValue(
|
|
HKEY_LOCAL_MACHINE,
|
|
SCE_ROOT_PATH,
|
|
TEXT("PolicyChangedInSetup")
|
|
);
|
|
|
|
if ( rc != ERROR_SUCCESS &&
|
|
rc != ERROR_FILE_NOT_FOUND &&
|
|
rc != ERROR_PATH_NOT_FOUND ) {
|
|
|
|
// if can't delete the value, set the value to 0
|
|
ScepRegSetIntValue( HKEY_LOCAL_MACHINE,
|
|
SCE_ROOT_PATH,
|
|
TEXT("PolicyChangedInSetup"),
|
|
0
|
|
);
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
// *********************************************************
|
|
// SAM policy change notifications
|
|
// procedures required by SAM notify mechanism
|
|
//
|
|
// *********************************************************
|
|
BOOLEAN
|
|
WINAPI
|
|
InitializeChangeNotify()
|
|
{
|
|
// inidicate this DLL support notifcation routines
|
|
// nothing special to be initialized
|
|
|
|
NTSTATUS NtStatus;
|
|
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
|
|
|
|
NtStatus = RtlAllocateAndInitializeSid(
|
|
&NtAuthority,
|
|
1,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&BuiltinDomainSid
|
|
);
|
|
return(TRUE);
|
|
}
|
|
|
|
BOOL
|
|
UninitializeChangeNotify()
|
|
{
|
|
|
|
if ( BuiltinDomainSid ) {
|
|
|
|
RtlFreeSid(BuiltinDomainSid);
|
|
BuiltinDomainSid = NULL;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
NTSTATUS
|
|
WINAPI
|
|
DeltaNotify(
|
|
IN PSID DomainSid,
|
|
IN SECURITY_DB_DELTA_TYPE DeltaType,
|
|
IN SECURITY_DB_OBJECT_TYPE ObjectType,
|
|
IN ULONG ObjectRid,
|
|
IN PUNICODE_STRING ObjectName OPTIONAL,
|
|
IN PLARGE_INTEGER ModifiedCount,
|
|
IN PSAM_DELTA_DATA DeltaData OPTIONAL
|
|
)
|
|
/*
|
|
Routine Description:
|
|
|
|
SAM policy change notification routine. Prototype and procedure
|
|
names are all required by SAM (see ntsam.h)
|
|
|
|
Arguments:
|
|
|
|
Similar to SceNotifyPolicyDelta
|
|
|
|
Return:
|
|
|
|
NT Status (should always return success)
|
|
*/
|
|
{
|
|
if ( DomainSid == NULL ) {
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// we don't track other SAM policies except the domain
|
|
// policy (password policy and account policy)
|
|
//
|
|
|
|
switch (DeltaType) {
|
|
case SecurityDbNew:
|
|
case SecurityDbChange:
|
|
if ( ObjectType != SecurityDbObjectSamDomain )
|
|
return STATUS_SUCCESS;
|
|
break;
|
|
|
|
case SecurityDbDelete:
|
|
|
|
//
|
|
// handle account deletion notifications
|
|
//
|
|
if ( ObjectType != SecurityDbObjectSamUser &&
|
|
ObjectType != SecurityDbObjectSamGroup &&
|
|
ObjectType != SecurityDbObjectSamAlias ) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
// unknown delta type
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if ( BuiltinDomainSid &&
|
|
RtlEqualSid( DomainSid, BuiltinDomainSid ) ) {
|
|
//
|
|
// no policy to filter in the BUILTIN domain
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// SAM filter and SCE SAM area policy propagation should be mutually exclusive
|
|
//
|
|
// only test (NOT wait) with timeout = 0 based on the event's state
|
|
// if event is not set (server is in SAM policy prop config)
|
|
// drop this change
|
|
// else
|
|
// continue filtering this SAM change
|
|
//
|
|
HANDLE hEventSamFilterPolicyPropSync = NULL;
|
|
DWORD rc=ERROR_SUCCESS;
|
|
|
|
hEventSamFilterPolicyPropSync = OpenEvent(
|
|
SYNCHRONIZE,
|
|
FALSE,
|
|
SCEP_SAM_FILTER_POLICY_PROP_EVENT
|
|
);
|
|
|
|
if (hEventSamFilterPolicyPropSync) {
|
|
|
|
rc = WaitForSingleObjectEx(
|
|
hEventSamFilterPolicyPropSync,
|
|
0,
|
|
FALSE
|
|
);
|
|
|
|
CloseHandle(hEventSamFilterPolicyPropSync);
|
|
hEventSamFilterPolicyPropSync = NULL;
|
|
|
|
if ( rc != WAIT_OBJECT_0 ) {
|
|
//
|
|
// we treat all other non-signalled return codes conservatively
|
|
//
|
|
|
|
DbgPrint("\n Dropping SAM notification because of Policy Propagation lock\n");
|
|
|
|
|
|
return rc;
|
|
}
|
|
|
|
//
|
|
// event is signaled by server - policy prop SAM config is over
|
|
//
|
|
}
|
|
|
|
|
|
DWORD dwPolicyFilterOff=0;
|
|
|
|
ScepRegQueryIntValue(
|
|
HKEY_LOCAL_MACHINE,
|
|
SCE_ROOT_PATH,
|
|
TEXT("PolicyFilterOff"),
|
|
&dwPolicyFilterOff
|
|
);
|
|
|
|
if ( dwPolicyFilterOff ) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
PSID AccountSid=NULL;
|
|
|
|
if ( SecurityDbDelete == DeltaType ) {
|
|
|
|
//
|
|
// handle account deletion notifications
|
|
//
|
|
|
|
if ( !NT_SUCCESS(ScepDomainIdToSid( DomainSid, ObjectRid, &AccountSid) ) ) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
(VOID) ScepNotifySaveInPolicyStorage(SecurityDbSam,
|
|
DeltaType,
|
|
ObjectType,
|
|
AccountSid
|
|
);
|
|
|
|
if ( AccountSid ) {
|
|
ScepFree(AccountSid);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SceOpenPolicy()
|
|
/*
|
|
Description:
|
|
|
|
This function is called to determine if LSA policy can be opened for
|
|
exclusive access.
|
|
|
|
This function will check SCE policy notification count and see if there
|
|
is any pending notifications that have not been added to the queue on the server
|
|
|
|
Note when this function is called by LSA, the WritePolicySemaphore in LSA
|
|
has been locked for write so other writes can't modify any policy in this window.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS indicates the count is successfully checked and it's 0.
|
|
STATUS_TIMEOUT indicates the queue is not empty or it fails to check the queue.
|
|
|
|
*/
|
|
{
|
|
|
|
//
|
|
// check the global count
|
|
//
|
|
NTSTATUS Status=STATUS_TIMEOUT;
|
|
DWORD cnt=0;
|
|
|
|
while ( TRUE ) {
|
|
|
|
EnterCriticalSection(&PolicyNotificationSync);
|
|
|
|
if ( SceNotifyCount == 0 ) {
|
|
//
|
|
// there is no pending notification
|
|
//
|
|
if ( STATUS_SUCCESS == Status ) {
|
|
//
|
|
// double check for the count
|
|
//
|
|
LeaveCriticalSection(&PolicyNotificationSync);
|
|
|
|
break;
|
|
|
|
} else {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
} else
|
|
Status = STATUS_TIMEOUT;
|
|
|
|
LeaveCriticalSection(&PolicyNotificationSync);
|
|
|
|
cnt++;
|
|
if ( cnt > 10 ) { // timeout 1 second.
|
|
break;
|
|
}
|
|
Sleep(100); // sleep for .1 second
|
|
|
|
}
|
|
|
|
if ( STATUS_SUCCESS != Status ) {
|
|
|
|
ScepNotifyFailureLog(0,
|
|
0,
|
|
SceNotifyCount,
|
|
(DWORD)Status,
|
|
L"SceOpenPolicy"
|
|
);
|
|
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|