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.
499 lines
13 KiB
499 lines
13 KiB
/*++
|
|
|
|
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;
|
|
}
|