501 lines
13 KiB
C++
501 lines
13 KiB
C++
/*++
|
||
|
||
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))) +
|
||
RtlLengthSid( LocalSystemSid ) +
|
||
RtlLengthSid( WorldSid ) +
|
||
8; // 8 is for good measure
|
||
|
||
*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;
|
||
}
|