Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

866 lines
25 KiB

/*++
Copyright (c) 1994 Microsoft Corporation
Module Name:
removabl.c
Abstract:
Removable Media Services Module - This module contains services
related to the protection of removable media (e.g., floppies
and CD Roms) related to logon and logoff.
Author:
Jim Kelly (JimK) Oct-19-1994
Environment:
Part of Winlogon.
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
////////////////////////////////// Section ////////////////////////////////
// //
// Constants used in this module only. //
// //
/////////////////////////////////////////////////////////////////////////////
#define RMVP_DRIVE_CD_ROM (1)
#define RMVP_DRIVE_FLOPPY (2)
//
// Size of the buffer used to enumerate object directory
// contents
//
#define RMVP_OBJDIR_ENUM_BUFFER_SIZE (1024)
////////////////////////////////// Section ////////////////////////////////
// //
// Variables Global to this module. //
// //
/////////////////////////////////////////////////////////////////////////////
PSID
RmvpLocalSystemSid,
RmvpAdministratorsSid;
//
// flags indicating whether or not we are configured to allocate
// floppies and/or CDRoms.
//
BOOLEAN
RmvpAllocateFloppies = FALSE,
RmvpAllocateCDRoms = FALSE;
////////////////////////////////// Section ////////////////////////////////
// //
// Prototypes of modules private to this module //
// //
/////////////////////////////////////////////////////////////////////////////
VOID
RmvpAllocateDevice(
IN HANDLE DeviceHandle,
IN PUNICODE_STRING DeviceName,
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN PUNICODE_STRING FsName,
IN OUT PHANDLE FsHandle
);
BOOLEAN
RmvpOpenDevice(
IN HANDLE Root,
IN POBJECT_DIRECTORY_INFORMATION Object,
IN ULONG DeviceType,
OUT PHANDLE Device
);
////////////////////////////////// Section ////////////////////////////////
// //
// Services Exported By This Module //
// //
/////////////////////////////////////////////////////////////////////////////
VOID
RmvInitializeRemovableMediaSrvcs(
VOID
)
/*++
Routine Description:
Initialize RemovableMediaServices Module
Arguments:
None.
Return Value:
None.
--*/
{
NTSTATUS
Status;
SID_IDENTIFIER_AUTHORITY
NtAuthority = SECURITY_NT_AUTHORITY;
//
// See if we need to protect floppy and/or CDRom drives.
//
if (GetProfileInt( TEXT("Winlogon"), TEXT("AllocateFloppies"), 0) == 1) {
RmvpAllocateFloppies = TRUE;
}
if (GetProfileInt( TEXT("Winlogon"), TEXT("AllocateCDRoms"), 0) == 1) {
RmvpAllocateCDRoms = TRUE;
}
//
// Initialize needed well-known sids
//
Status = RtlAllocateAndInitializeSid( &NtAuthority,
1,
SECURITY_LOCAL_SYSTEM_RID,
0, 0, 0, 0, 0, 0, 0,
&RmvpLocalSystemSid
);
ASSERT(NT_SUCCESS(Status));
Status = RtlAllocateAndInitializeSid( &NtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&RmvpAdministratorsSid
);
ASSERT(NT_SUCCESS(Status));
return;
}
VOID
RmvAllocateRemovableMedia(
IN PSID AllocatorId
)
/*++
Routine Description:
This routine must be called during the logon process.
Its purpose is to:
See if floppy and / or CD Rom allocation is enabled.
If so, then allocate the devices by:
Restricting TRAVERSE access to the devices, and
Forcing all handles to volumes open on those devices
to be made invalid.
Note that the work of finding and openning these devices must
be done at logon time rather than system initialization time
because of NT's ability to dynamically add devices while running.
Arguments:
AllocatorId - Pointer to the SID to which the devices are
to be allocated.
Return Value:
None.
--*/
{
NTSTATUS
Status,
IgnoreStatus;
ACCESS_MASK
Access;
MYACE
Ace[3];
ACEINDEX
AceCount = 0;
PSECURITY_DESCRIPTOR
SecurityDescriptor;
ULONG
DriveType,
ReturnedLength,
Context = 0;
CHAR
Buffer[RMVP_OBJDIR_ENUM_BUFFER_SIZE];
HANDLE
DeviceHandle,
FatHandle = NULL,
CdfsHandle = NULL,
DirectoryHandle;
UNICODE_STRING
DirectoryName,
CdfsName,
FatfsName;
OBJECT_ATTRIBUTES
Attributes;
BOOLEAN
Opened;
POBJECT_DIRECTORY_INFORMATION
DirInfo;
RtlInitUnicodeString( &CdfsName, L"\\cdfs" );
RtlInitUnicodeString( &FatfsName, L"\\fat" );
//
// If there is nothing to allocate, then return.
//
if (!RmvpAllocateFloppies && !RmvpAllocateCDRoms) {
return;
}
///////////////////////////////////////////////////////////////////////
//
// Technical Note:
//
// If I understand the way things work correctly, then there is
// a race condition that exists that we have to work around.
//
// The problem is that until a volume is mounted in a file-system
// that file system might not be loaded (and a corresponding device
// object may not exist). Therefore, we must not try to open a
// filesystem until we have opened a device object. For example,
// don't open FAT until we have opened \device\floppy0 AND PROTECTED
// IT AGAINST FUTURE TRAVERSE ACCESS.
//
// So, if you are looking at this routine and wondering why I don't
// open the FAT and CDFS file systems right away, it is in case they
// aren't loaded yet, but someone manages to squeek in a volume mount
// after I try to open the filesystems.
//
// What I believe will work is to open any floppy or cdrom devices
// I find, then change who has traverse access to them, then open
// the corresponding filesystem object IF IT HASN'T ALREADY BEEN
// OPENED. Once a filesystem object is opened, there is no need
// to close it and re-open it.
//
///////////////////////////////////////////////////////////////////////
//
// Set up the protection to be applied to the removable device objects
//
// System: FILE_READ_ATTRIBUTES | READ_CONTROL | WRITE_DAC | SYNCHRONIZE
// AllocationId: RWE
//
// We apply the restrictive System access to the object so that network shares
// don't gain access (by nature of using a handle to the volume root
// that LM server keeps).
//
Access = FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE;
SetMyAce( &(Ace[AceCount]), AllocatorId, Access, 0 );
AceCount++;
//Access &= ~FILE_TRAVERSE;
Access = (FILE_READ_ATTRIBUTES | READ_CONTROL | WRITE_DAC | SYNCHRONIZE);
SetMyAce( &(Ace[AceCount]), RmvpLocalSystemSid, Access, 0 );
AceCount++;
#ifdef TRMV
{
//
// This is needed for our test program (an interactive program
// run in user mode) so that we don't set protection on the device
// preventing us from changing the protection.
//
// THIS CODE IS NOT INCLUDED WHEN THIS MODULE IS PART OF WINLOGON.
//
ACCESS_MASK
TestorAccess = (FILE_READ_ATTRIBUTES | READ_CONTROL | WRITE_DAC | SYNCHRONIZE);
SetMyAce( &(Ace[AceCount]), RmvpAdministratorsSid, TestorAccess, 0 );
AceCount++;
}
#endif // TEST REMOVABLE
// Check we didn't goof
ASSERT((sizeof(Ace) / sizeof(MYACE)) >= AceCount);
//
// Create the security descriptor
//
SecurityDescriptor = CreateSecurityDescriptor(Ace, AceCount);
if (SecurityDescriptor != NULL) {
//
// We find floppies and or CD Roms by enumerating the objects in
// the "\Device" object container and selecting the ones that
// start with "floppy" followed by a number or "cdrom" followed
// by a number. Even after finding such an object, we want to be
// sure it is a Device object and then open it and query it to
// make sure it is really a floppy or cdrom. The reason I say this
// is because you will find names like "floppyControllerEvent" in
// the \device container.
//
RtlInitUnicodeString( &DirectoryName, L"\\Device" );
InitializeObjectAttributes( &Attributes,
&DirectoryName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL );
Status = NtOpenDirectoryObject( &DirectoryHandle,
STANDARD_RIGHTS_READ |
DIRECTORY_QUERY |
DIRECTORY_TRAVERSE,
&Attributes );
#if DBG
if (!NT_SUCCESS(Status)) {
DebugLog((DEB_ERROR, "Failed to open \\Device object container. Status: %#x\n", Status ));
}
#endif //DBG
ASSERT(NT_SUCCESS(Status));
//
// Loop through the directory querying device objects
//
Status = NtQueryDirectoryObject( DirectoryHandle,
Buffer,
sizeof(Buffer),
TRUE, // one entry at a time for now
TRUE,
&Context,
&ReturnedLength );
#if DBG
if (!NT_SUCCESS( Status )) {
if (Status != STATUS_NO_MORE_ENTRIES) {
DebugLog((DEB_ERROR, "failed to query directory object, status: 0x%lx\n", Status));
}
}
#endif //DBG
while ( NT_SUCCESS(Status) ) {
//
// Point to the first record in the buffer, we are guaranteed to have
// one otherwise Status would have been NO_MORE_ENTRIES
//
DirInfo = (POBJECT_DIRECTORY_INFORMATION)Buffer;
while (DirInfo->Name.Length != 0) {
//
// For every record in the buffer compare it's name with the ones we're
// looking for
//
if (RmvpAllocateFloppies) {
Opened = RmvpOpenDevice( DirectoryHandle,
DirInfo,
RMVP_DRIVE_FLOPPY,
&DeviceHandle
);
if (Opened) {
RmvpAllocateDevice( DeviceHandle, &DirInfo->Name, SecurityDescriptor, &FatfsName, &FatHandle );
NtClose( DeviceHandle );
}
}
if (RmvpAllocateCDRoms) {
Opened = RmvpOpenDevice( DirectoryHandle,
DirInfo,
RMVP_DRIVE_CD_ROM,
&DeviceHandle
);
if (Opened) {
RmvpAllocateDevice( DeviceHandle, &DirInfo->Name, SecurityDescriptor, &CdfsName, &CdfsHandle );
NtClose( DeviceHandle );
}
}
//
// Advance DirInfo to the next entry
//
DirInfo ++;
} //end_while (more names in buffer)
//
// Get the next block of entries
//
Status = NtQueryDirectoryObject( DirectoryHandle,
Buffer,
sizeof(Buffer),
// LATER FALSE,
TRUE, // one entry at a time for now
FALSE,
&Context,
&ReturnedLength );
} //end_while (enumeration succeeded)
//
// We no longer need handles to FAT or CDFS (if they were opened).
//
if (FatHandle != NULL) {
IgnoreStatus = NtClose( FatHandle );
ASSERT(NT_SUCCESS(IgnoreStatus));
}
if (CdfsHandle != NULL) {
IgnoreStatus = NtClose( CdfsHandle );
ASSERT(NT_SUCCESS(IgnoreStatus));
}
//
// Free up the security descriptor
//
DeleteSecurityDescriptor(SecurityDescriptor);
} else {
DebugLog((DEB_ERROR, "failed to create removable media security descriptor"));
}
return;
}
////////////////////////////////// Section ////////////////////////////////
// //
// Services Private To This Module //
// //
/////////////////////////////////////////////////////////////////////////////
VOID
RmvpAllocateDevice(
IN HANDLE DeviceHandle,
IN PUNICODE_STRING DeviceName,
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN PUNICODE_STRING FsName,
IN OUT PHANDLE FsHandle
)
/*++
Routine Description:
This routine attempts to allocate the device opened by DeviceHandle.
Allocation entails changing the protection on the device (the new
protection is passed in, but is expected to restrict TRAVERSE access),
and notifying the filesystem to invalidate all handles to the
volumes on the device.
Arguments:
DeviceHandle - handle to the device being allocated. This must be opened
for WRITE_DAC access.
DeviceName - The name of the device being allocated.
SecurityDescriptor - New protection to assign to the device. Only the
Dacl of this security descriptor is used.
FsName - The name of the file system that owns responsibility for the
device. This is expected to be either "\\fat" or
"\\cdfs".
FsHandle - Pointer to a handle to the filesystem. If the handle
value is NULL, then it is assumed that the filesystem has not yet
been opened. In that case, this routine will attempt to open the
filesystem after assigning protection to the device.
Return Value:
TRUE - indicates the device was successfully opened.
FALSE - indicates the device was NOT successfully opened.
--*/
{
NTSTATUS
Status;
OBJECT_ATTRIBUTES
Attributes;
IO_STATUS_BLOCK
IoStatusBlock;
BOOLEAN
WasEnabled;
//
// Change the protection on the device to restrict traverse access.
//
Status = NtSetSecurityObject( DeviceHandle, DACL_SECURITY_INFORMATION, SecurityDescriptor);
ASSERT(NT_SUCCESS(Status));
//
// If the File system isn't yet open, then do so now.
//
if ((*FsHandle) == NULL) {
//
// Turn on the TCB privilege for our open to the filesystem to work.
//
Status = RtlAdjustPrivilege( SE_TCB_PRIVILEGE,
TRUE, // Enable
FALSE, // Client
&WasEnabled
);
ASSERT(NT_SUCCESS(Status));
if (NT_SUCCESS(Status)) {
InitializeObjectAttributes( &Attributes,
FsName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL );
Status = NtOpenFile( FsHandle,
(SYNCHRONIZE | FILE_READ_ATTRIBUTES),
&Attributes,
&IoStatusBlock,
(FILE_SHARE_READ | FILE_SHARE_WRITE),
FILE_SYNCHRONOUS_IO_NONALERT
);
if (!NT_SUCCESS(Status)) {
//
// If no volumes have ever been mounted in the file system,
// then the file system may not be loaded. So, this error
// can not be considered serious.
//
(*FsHandle) = NULL; // In case garbage was put in our out parameter
if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
DebugLog((DEB_ERROR, "Failed to open <%wZ>. Status: %#x\n", FsName, Status));
}
}
//
// Now restore the privilege (if necessary)
//
if (!WasEnabled) {
Status = RtlAdjustPrivilege( SE_TCB_PRIVILEGE,
FALSE, // Disable
FALSE, // Client
&WasEnabled
);
ASSERT(NT_SUCCESS(Status));
}
}
}
if ((*FsHandle) != NULL) {
//
// Notify the file system that it needs to invalidate the
// passed in volume.
//
Status = NtFsControlFile( (*FsHandle),
NULL,
NULL,
NULL,
&IoStatusBlock,
FSCTL_INVALIDATE_VOLUMES,
&DeviceHandle, //DavidGoe is going to define a real structure to pass here
sizeof(HANDLE),
NULL,
0 );
#if DBG
if (!NT_SUCCESS(Status)) {
DebugLog((DEB_ERROR, "Failed to remove handles from:\n"
" device: <%wZ>\n"
" file system: <%wZ>\n"
" Status: 0x%lx\n\n",
DeviceName, FsName, Status));
ASSERT(NT_SUCCESS(Status));
}
#endif //DBG
}
return;
}
BOOLEAN
RmvpOpenDevice(
IN HANDLE Root,
IN POBJECT_DIRECTORY_INFORMATION Object,
IN ULONG DeviceType,
OUT PHANDLE Device
)
/*++
Routine Description:
This routine is called to open a handle to a device whose name starts
with the string specified in the Prefix parameter.
Arguments:
Root - handle to an object directory to be used as the root
when openning the device object. That is, the name specified
in the Object parameter is relative to the object this handle
refers to.
Object - Contains the name of the object and the name of the type
of the object. If the name of the type is not "device" and the
name of the object does not start with the string passed in
the Prefix parameter, then the open is not attempted.
DeviceType - specifies the type of device the object is expected to
be. Valid values are:
RMVP_DRIVE_CD_ROM
RMVP_DRIVE_FLOPPY
Device - Receives the handle to the device object (upon success).
Return Value:
TRUE - indicates the device was successfully opened.
FALSE - indicates the device was NOT successfully opened.
--*/
{
NTSTATUS
IgnoreStatus,
Status;
OBJECT_ATTRIBUTES
ObjectAttributes;
IO_STATUS_BLOCK
IoStatusBlock;
UNICODE_STRING
Prefix,
DeviceName;
HANDLE
FileHandle;
FILE_FS_DEVICE_INFORMATION
DeviceInfo;
//RmvpPrint("Attempting to open Object<%wZ>, Type<%wZ>\n", &Object->Name, &Object->TypeName);
RtlInitUnicodeString( &DeviceName, L"Device" );
//
// If the type is not "Device" then we aren't interested in this object
//
if (!RtlEqualUnicodeString(&Object->TypeName, &DeviceName, TRUE)) {
return(FALSE);
}
//
// If the object name doesn't have the right prefix, then we aren't
// interested in this object
//
if (DeviceType == RMVP_DRIVE_CD_ROM) {
RtlInitUnicodeString( &Prefix, L"cdrom" );
if (!RtlPrefixUnicodeString( &Prefix, &Object->Name, TRUE)) {
return(FALSE);
}
} else {
ASSERT( DeviceType == RMVP_DRIVE_FLOPPY);
RtlInitUnicodeString( &Prefix, L"floppy" );
if (!RtlPrefixUnicodeString( &Prefix, &Object->Name, TRUE)) {
return(FALSE);
}
}
//
// Looks promising. Open the Device object
//
InitializeObjectAttributes( &ObjectAttributes,
&Object->Name,
OBJ_CASE_INSENSITIVE,
Root,
NULL
);
Status = NtOpenFile( &FileHandle,
(ACCESS_MASK)FILE_READ_ATTRIBUTES |
WRITE_DAC |
READ_CONTROL |
SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_NONALERT
);
#if DBG
if (!NT_SUCCESS(Status)) {
DebugLog((DEB_ERROR, "Can't open for allocate: Object<%wZ> Type<%wZ>\n"
" Status: 0x%lx\n", &Object->Name, &Object->TypeName, Status));
}
#endif //DBG
if (NT_SUCCESS( Status )) {
Status = NtQueryVolumeInformationFile( FileHandle,
&IoStatusBlock,
&DeviceInfo,
sizeof( DeviceInfo ),
FileFsDeviceInformation
);
if (NT_SUCCESS( Status )) {
//
// See if its the right kind of beast and has the right name prefix
//
if (DeviceType == RMVP_DRIVE_CD_ROM) {
if (DeviceInfo.DeviceType == FILE_DEVICE_CD_ROM) {
(*Device) = FileHandle;
DebugLog((DEB_TRACE, "Winlogon: Allocating CD Rom:\n"
" object: <%wZ>\n"
" type: <%wZ>\n",
&Object->Name, &Object->TypeName ));
return(TRUE);
}
} else {
ASSERT( DeviceType == RMVP_DRIVE_FLOPPY);
if ((DeviceInfo.Characteristics & FILE_FLOPPY_DISKETTE)) {
(*Device) = FileHandle;
DebugLog((DEB_TRACE, "Winlogon: Allocating floppy:\n"
" object: <%wZ>\n"
" type: <%wZ>\n",
&Object->Name, &Object->TypeName ));
return(TRUE);
}
}
}
IgnoreStatus = NtClose(FileHandle);
ASSERT(NT_SUCCESS(IgnoreStatus));
}
return(FALSE);
}