|
|
/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
scbsm.cxx
Abstract:
This module implements a simplistic scheme to have a trusted system component issue a BroadcastSystemMessage when a network drive is added or deleted through the WNet APIs. This scheme guarantees that a spurious message is never sent; however, there are some situations in which a bona fide message can get lost or delayed. The real solution requires Plug and Play support from network providers.
The following functions are in this file: ScInitBSM ScHandleBSMRequest ScGetNetworkDrives ScCreateBSMEventSD
Author:
Anirudh Sahni (anirudhs) 05-Jun-1996
Environment:
User Mode - Win32
Notes:
There is no architectural reason for this to be in the service controller. A more appropriate place would be the Plug and Play service.
Revision History:
22-Oct-1998 jschwart Converted SCM to use NT thread pool APIs
05-Jun-1996 AnirudhS Created.
--*/
#include "precomp.hxx"
#include <dbt.h> // BroadcastSystemMessage structures
#include <winsvcp.h> // SC_BSM_EVENT_NAME
#include <scseclib.h> // well-known SIDs
#include "scbsm.h"
//-------------------------------------------------------------------//
// //
// Constants and Macros //
// //
//-------------------------------------------------------------------//
//-------------------------------------------------------------------//
// //
// Static global variables //
// //
//-------------------------------------------------------------------//
//
// Event that will be pulsed by the WNet APIs when they want a message
// broadcast
//
HANDLE hBSMEvent;
//
// What the net drive bitmask was when we last broadcast (initially 0)
//
DWORD LastNetDrives;
//
// Work item handle
//
HANDLE g_hWorkitem;
//-------------------------------------------------------------------//
// //
// Local function prototypes //
// //
//-------------------------------------------------------------------//
BOOL ScCreateBSMEventSD( PSECURITY_DESCRIPTOR * SecurityDescriptor );
VOID ScHandleBSMRequest( PVOID pContext, BOOLEAN dwWaitStatus );
DWORD ScGetNetworkDrives( );
//-------------------------------------------------------------------//
// //
// Functions //
// //
//-------------------------------------------------------------------//
VOID ScInitBSM( ) /*++
Routine Description:
This function performs initialization related to network drive arrival broadcasts.
CODEWORK Should we fail service controller initialization if this fails? Event log the cause?
Arguments:
None
Return Value:
None
--*/ { NTSTATUS ntStatus; SECURITY_ATTRIBUTES EventAttrs = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE };
//
// Create the security descriptor for the event.
// No-one else can wait for the event, but anyone can set it.
//
if (! ScCreateBSMEventSD(&EventAttrs.lpSecurityDescriptor)) { SC_LOG(ERROR, "Couldn't create BSM event security descriptor, %lu\n", GetLastError()); return; }
//
// Create the event that will be pulsed by the WNet APIs when they
// want a message broadcast.
//
hBSMEvent = CreateEvent( &EventAttrs, // security attrs
FALSE, // make this an auto-reset event
FALSE, // initial state is nonsignaled
SC_BSM_EVENT_NAME // name
);
if (hBSMEvent == NULL) { SC_LOG(ERROR, "Couldn't create BSM event, %lu\n", GetLastError()); }
LocalFree(EventAttrs.lpSecurityDescriptor);
if (hBSMEvent == NULL) { return; }
//
// Add the work item that will be executed when this event is signaled.
//
ntStatus = RtlRegisterWait(&g_hWorkitem, // work item handle
hBSMEvent, // waitable object handle
ScHandleBSMRequest, // callback function
0, // parameter for callback function
INFINITE, // infinite wait
WT_EXECUTEONLYONCE); // one-time work item
if (!NT_SUCCESS(ntStatus)) { SC_LOG1(ERROR, "ScInitBSM: RtlRegisterWait failed 0x%x\n", ntStatus); CloseHandle(hBSMEvent); hBSMEvent = NULL; } }
VOID ScHandleBSMRequest( PVOID pContext, BOOLEAN fWaitStatus ) /*++
Routine Description:
This is the callback function executed when some process sets the BSM Request event.
Arguments:
Return Value:
--*/ { DWORD NetDrives; NTSTATUS ntStatus;
DEV_BROADCAST_VOLUME dbv; LONG status;
//
// If fWaitStatus if TRUE, we're being signalled because of a timeout.
// Since we registered with an infinite wait, this should NEVER happen
//
ASSERT(fWaitStatus == FALSE);
SC_LOG0(BSM, "Handling a BSM request\n");
//
// Deregister the work item (we have to do this even if the
// WT_EXECUTEONLYONCE flag is specified)
//
ntStatus = RtlDeregisterWait(g_hWorkitem);
if (!NT_SUCCESS(ntStatus)) {
SC_LOG1(ERROR, "ScHandleBSMRequest: RtlDeregisterWait FAILED %#x\n", ntStatus); }
//
// Keep broadcasting until the set of net drives stops changing
//
for (;;) { //
// If we're shutting down, do nothing
//
if (ScShutdownInProgress) { return; }
//
// Get the current net drive bitmask and compare against the net
// drive bitmask when we last broadcast
//
NetDrives = ScGetNetworkDrives();
SC_LOG2(BSM, "Previous net drives: %#lx Now: %#lx\n", LastNetDrives, NetDrives);
if (NetDrives == LastNetDrives) { break; }
//
// Broadcast about deleted volumes
//
dbv.dbcv_size = sizeof(dbv); dbv.dbcv_devicetype = DBT_DEVTYP_VOLUME; dbv.dbcv_reserved = 0; dbv.dbcv_unitmask = LastNetDrives & ~NetDrives; dbv.dbcv_flags = DBTF_NET; if (dbv.dbcv_unitmask != 0) { DWORD dwRec = BSM_APPLICATIONS | BSM_ALLDESKTOPS; SC_LOG0(BSM, "Calling BroadcastSystemMessage...\n"); status = BroadcastSystemMessage( BSF_FORCEIFHUNG | BSF_NOHANG | BSF_NOTIMEOUTIFNOTHUNG, &dwRec, WM_DEVICECHANGE, (WPARAM) DBT_DEVICEREMOVECOMPLETE, (LPARAM)(DEV_BROADCAST_HDR*)(&dbv) ); SC_LOG0(BSM, "... returned\n");
if (status <= 0) { SC_LOG2(ERROR, "BSM for deleted volumes %#lx FAILED, returned %ld\n", dbv.dbcv_unitmask, status); } }
//
// Broadcast about added volumes
//
dbv.dbcv_unitmask = NetDrives & ~LastNetDrives; if (dbv.dbcv_unitmask != 0) { DWORD dwRec = BSM_APPLICATIONS | BSM_ALLDESKTOPS; SC_LOG0(BSM, "Calling BroadcastSystemMessage...\n");
status = BroadcastSystemMessage( BSF_FORCEIFHUNG | BSF_NOHANG | BSF_NOTIMEOUTIFNOTHUNG, &dwRec, WM_DEVICECHANGE, (WPARAM) DBT_DEVICEARRIVAL, (LPARAM)(DEV_BROADCAST_HDR*)(&dbv) );
SC_LOG0(BSM, "... returned\n");
if (status <= 0) { SC_LOG2(ERROR, "BSM for added volumes %#lx FAILED, returned %ld\n", dbv.dbcv_unitmask, status); } }
//
// Remember the drive set that we last broadcast about
//
LastNetDrives = NetDrives;
//
// Go around the loop again to detect changes that may have occurred
// while we were broadcasting
//
}
//
// Add this work item back to the queue
//
SC_LOG0(BSM, "Re-waiting on BSM event\n");
ntStatus = RtlRegisterWait(&g_hWorkitem, // work item handle
hBSMEvent, // waitable object handle
ScHandleBSMRequest, // callback function
0, // parameter for callback function
INFINITE, // infinite wait
WT_EXECUTEONLYONCE); // one-time work item
if (!NT_SUCCESS(ntStatus)) { SC_LOG1(ERROR, "ScInitBSM: RtlRegisterWait failed 0x%x\n", ntStatus); // CloseHandle(hBSMRequest);
// hBSMRequest = NULL;
// BUGBUG No more events will be processed. Event log this?
}
return; }
DWORD ScGetNetworkDrives( ) /*++
Routine Description:
Returns a drive bitmask similar to GetLogicalDrives, but including only the network drives.
Arguments:
Return Value:
--*/ {
WCHAR wszDrive[] = L" :\\"; DWORD dwMask = 0; DWORD dwCurrDrive = 1;
// For all the drives
for (wszDrive[0] = L'A'; wszDrive[0] <= L'Z'; wszDrive[0]++, dwCurrDrive <<= 1) { if (GetDriveType(wszDrive) == DRIVE_REMOTE) { dwMask |= dwCurrDrive; } }
return dwMask; }
BOOL ScCreateBSMEventSD( PSECURITY_DESCRIPTOR * SecurityDescriptor ) /*++
Routine Description:
This function creates a security descriptor for the BSM request event. It grants EVENT_ALL_ACCESS to local system and EVENT_MODIFY_STATE access to the rest of the world. This prevents principals other than local system from waiting for the event.
Arguments:
SecurityDescriptor - Receives a pointer to the new security descriptor. Should be freed with LocalFree.
Return Value:
TRUE - success
FALSE - failure, use GetLastError
History:
AnirudhS 06-Jun-1996 Adapted from LsapAuCreatePortSD in auloop.c
--*/ { NTSTATUS Status; ULONG AclLength; PACL EventDacl;
//
// Allocate a buffer to contain the SD followed by the DACL
// Note, the well-known SIDs are expected to have been created
// by this time
//
AclLength = (ULONG) sizeof(ACL) + (2 * ((ULONG)sizeof(ACCESS_ALLOWED_ACE) - sizeof(ULONG))) + RtlLengthSid( LocalSystemSid ) + RtlLengthSid( WorldSid );
*SecurityDescriptor = (PSECURITY_DESCRIPTOR) LocalAlloc( 0, SECURITY_DESCRIPTOR_MIN_LENGTH + AclLength );
if (*SecurityDescriptor == NULL) { return FALSE; }
EventDacl = (PACL) ((BYTE*)(*SecurityDescriptor) + SECURITY_DESCRIPTOR_MIN_LENGTH);
//
// Set up a default ACL
//
// Public: WORLD:EVENT_MODIFY_STATE, SYSTEM:all
Status = RtlCreateAcl( EventDacl, AclLength, ACL_REVISION2);
//
// WORLD access
//
Status = RtlAddAccessAllowedAce ( EventDacl, ACL_REVISION2, EVENT_MODIFY_STATE, WorldSid ); SC_ASSERT( NT_SUCCESS(Status) );
//
// SYSTEM access
//
Status = RtlAddAccessAllowedAce ( EventDacl, ACL_REVISION2, EVENT_ALL_ACCESS, LocalSystemSid ); SC_ASSERT( NT_SUCCESS(Status) );
//
// Now initialize security descriptors
// that export this protection
//
Status = RtlCreateSecurityDescriptor( *SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION1 ); SC_ASSERT( NT_SUCCESS(Status) ); Status = RtlSetDaclSecurityDescriptor( *SecurityDescriptor, TRUE, // DaclPresent
EventDacl, FALSE // DaclDefaulted
); SC_ASSERT( NT_SUCCESS(Status) );
return TRUE; }
|