/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS Base API Server DLL * FILE: subsystems/win/basesrv/dosdev.c * PURPOSE: DOS Devices Management * PROGRAMMERS: Pierre Schweitzer (pierre.schweitzer@reactos.org) */ /* INCLUDES *******************************************************************/ #include "basesrv.h" #define NDEBUG #include /* GLOBALS ********************************************************************/ typedef struct _BASE_DOS_DEVICE_HISTORY_ENTRY { LIST_ENTRY Entry; UNICODE_STRING Device; UNICODE_STRING Target; } BASE_DOS_DEVICE_HISTORY_ENTRY, *PBASE_DOS_DEVICE_HISTORY_ENTRY; static RTL_CRITICAL_SECTION BaseDefineDosDeviceCritSec; static LIST_ENTRY DosDeviceHistory; /* PRIVATE FUNCTIONS **********************************************************/ VOID BaseInitDefineDosDevice(VOID) { RtlInitializeCriticalSection(&BaseDefineDosDeviceCritSec); InitializeListHead(&DosDeviceHistory); } VOID BaseCleanupDefineDosDevice(VOID) { PLIST_ENTRY Entry, ListHead; PBASE_DOS_DEVICE_HISTORY_ENTRY HistoryEntry; RtlDeleteCriticalSection(&BaseDefineDosDeviceCritSec); ListHead = &DosDeviceHistory; Entry = ListHead->Flink; while (Entry != ListHead) { HistoryEntry = (PBASE_DOS_DEVICE_HISTORY_ENTRY) CONTAINING_RECORD(Entry, BASE_DOS_DEVICE_HISTORY_ENTRY, Entry); Entry = Entry->Flink; if (HistoryEntry) { if (HistoryEntry->Target.Buffer) { RtlFreeHeap(BaseSrvHeap, 0, HistoryEntry->Target.Buffer); } if (HistoryEntry->Device.Buffer) { RtlFreeHeap(BaseSrvHeap, 0, HistoryEntry->Device.Buffer); } RtlFreeHeap(BaseSrvHeap, 0, HistoryEntry); } } } /* PUBLIC SERVER APIS *********************************************************/ CSR_API(BaseSrvDefineDosDevice) { NTSTATUS Status; PBASE_DEFINE_DOS_DEVICE DefineDosDeviceRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.DefineDosDeviceRequest; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE LinkHandle = NULL; UNICODE_STRING DeviceName = {0}; UNICODE_STRING RequestDeviceName = {0}; UNICODE_STRING LinkTarget = {0}; PUNICODE_STRING RequestLinkTarget; ULONG Length; SID_IDENTIFIER_AUTHORITY WorldAuthority = {SECURITY_WORLD_SID_AUTHORITY}; SID_IDENTIFIER_AUTHORITY SystemAuthority = {SECURITY_NT_AUTHORITY}; PSECURITY_DESCRIPTOR SecurityDescriptor; PACL Dacl; PSID AdminSid; PSID SystemSid; PSID WorldSid; ULONG SidLength; PBASE_DOS_DEVICE_HISTORY_ENTRY HistoryEntry; PLIST_ENTRY Entry; PLIST_ENTRY ListHead; BOOLEAN Matched, AddHistory; DWORD dwFlags; PWSTR lpBuffer; DPRINT("BaseSrvDefineDosDevice entered, Flags:%d, DeviceName:%wZ, TargetPath:%wZ\n", DefineDosDeviceRequest->Flags, &DefineDosDeviceRequest->DeviceName, &DefineDosDeviceRequest->TargetPath); Matched = AddHistory = FALSE; HistoryEntry = NULL; AdminSid = SystemSid = WorldSid = NULL; SecurityDescriptor = NULL; ListHead = &DosDeviceHistory; dwFlags = DefineDosDeviceRequest->Flags; /* Validate the flags */ if ( (dwFlags & 0xFFFFFFF0) || ((dwFlags & DDD_EXACT_MATCH_ON_REMOVE) && !(dwFlags & DDD_REMOVE_DEFINITION)) ) { return STATUS_INVALID_PARAMETER; } Status = RtlEnterCriticalSection(&BaseDefineDosDeviceCritSec); if (!NT_SUCCESS(Status)) { DPRINT1("RtlEnterCriticalSection() failed (Status %lx)\n", Status); return Status; } _SEH2_TRY { Status = RtlUpcaseUnicodeString(&RequestDeviceName, &DefineDosDeviceRequest->DeviceName, TRUE); if (!NT_SUCCESS(Status)) _SEH2_LEAVE; RequestLinkTarget = &DefineDosDeviceRequest->TargetPath; lpBuffer = (PWSTR)RtlAllocateHeap(BaseSrvHeap, HEAP_ZERO_MEMORY, RequestDeviceName.MaximumLength + 5 * sizeof(WCHAR)); if (!lpBuffer) { DPRINT1("Failed to allocate memory\n"); Status = STATUS_NO_MEMORY; _SEH2_LEAVE; } swprintf(lpBuffer, L"\\??\\%wZ", &RequestDeviceName); RtlInitUnicodeString(&DeviceName, lpBuffer); InitializeObjectAttributes(&ObjectAttributes, &DeviceName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenSymbolicLinkObject(&LinkHandle, DELETE | 0x1, &ObjectAttributes); if (NT_SUCCESS(Status)) { Status = NtQuerySymbolicLinkObject(LinkHandle, &LinkTarget, &Length); if (!NT_SUCCESS(Status) && Status == STATUS_BUFFER_TOO_SMALL) { LinkTarget.Length = 0; LinkTarget.MaximumLength = Length; LinkTarget.Buffer = (PWSTR) RtlAllocateHeap(BaseSrvHeap, HEAP_ZERO_MEMORY, Length); if (!LinkTarget.Buffer) { DPRINT1("Failed to allocate memory\n"); Status = STATUS_NO_MEMORY; _SEH2_LEAVE; } Status = NtQuerySymbolicLinkObject(LinkHandle, &LinkTarget, &Length); } if (!NT_SUCCESS(Status)) { DPRINT1("NtQuerySymbolicLinkObject(%wZ) failed (Status %lx)\n", &DeviceName, Status); _SEH2_LEAVE; } if ((dwFlags & DDD_REMOVE_DEFINITION)) { /* If no target name specified we remove the current symlink target */ if (RequestLinkTarget->Length == 0) Matched = TRUE; else { if (dwFlags & DDD_EXACT_MATCH_ON_REMOVE) Matched = !RtlCompareUnicodeString(RequestLinkTarget, &LinkTarget, TRUE); else Matched = RtlPrefixUnicodeString(RequestLinkTarget, &LinkTarget, TRUE); } if (Matched && IsListEmpty(ListHead)) { /* Current symlink target macthed and there is nothing to revert to */ RequestLinkTarget = NULL; } else if (Matched && !IsListEmpty(ListHead)) { /* * Fetch the first history entry we come across for the device name. * This will become the current symlink target for the device name. */ Matched = FALSE; Entry = ListHead->Flink; while (Entry != ListHead) { HistoryEntry = (PBASE_DOS_DEVICE_HISTORY_ENTRY) CONTAINING_RECORD(Entry, BASE_DOS_DEVICE_HISTORY_ENTRY, Entry); Matched = !RtlCompareUnicodeString(&RequestDeviceName, &HistoryEntry->Device, FALSE); if (Matched) { RemoveEntryList(&HistoryEntry->Entry); RequestLinkTarget = &HistoryEntry->Target; break; } Entry = Entry->Flink; HistoryEntry = NULL; } /* Nothing to revert to so delete the symlink */ if (!Matched) RequestLinkTarget = NULL; } else if (!Matched) { /* * Locate a previous symlink target as we did not get * a hit earlier. If we find one we need to remove it. */ Entry = ListHead->Flink; while (Entry != ListHead) { HistoryEntry = (PBASE_DOS_DEVICE_HISTORY_ENTRY) CONTAINING_RECORD(Entry, BASE_DOS_DEVICE_HISTORY_ENTRY, Entry); Matched = !RtlCompareUnicodeString(&RequestDeviceName, &HistoryEntry->Device, FALSE); if (!Matched) { HistoryEntry = NULL; Entry = Entry->Flink; continue; } Matched = FALSE; if (dwFlags & DDD_EXACT_MATCH_ON_REMOVE) { if (!RtlCompareUnicodeString(RequestLinkTarget, &HistoryEntry->Target, TRUE)) { Matched = TRUE; } } else if (RtlPrefixUnicodeString(RequestLinkTarget, &HistoryEntry->Target, TRUE)) { Matched = TRUE; } if (Matched) { RemoveEntryList(&HistoryEntry->Entry); break; } Entry = Entry->Flink; HistoryEntry = NULL; } /* Leave existing symlink as is */ if (!Matched) Status = STATUS_OBJECT_NAME_NOT_FOUND; else Status = STATUS_SUCCESS; _SEH2_LEAVE; } } else { AddHistory = TRUE; } Status = NtMakeTemporaryObject(LinkHandle); if (!NT_SUCCESS(Status)) { DPRINT1("NtMakeTemporaryObject(%wZ) failed (Status %lx)\n", &DeviceName, Status); _SEH2_LEAVE; } Status = NtClose(LinkHandle); LinkHandle = NULL; if (!NT_SUCCESS(Status)) { DPRINT1("NtClose(%wZ) failed (Status %lx)\n", &DeviceName, Status); _SEH2_LEAVE; } } /* Don't create symlink if we don't have a target */ if (!RequestLinkTarget || RequestLinkTarget->Length == 0) _SEH2_LEAVE; if (AddHistory) { HistoryEntry = (PBASE_DOS_DEVICE_HISTORY_ENTRY) RtlAllocateHeap(BaseSrvHeap, HEAP_ZERO_MEMORY, sizeof(BASE_DOS_DEVICE_HISTORY_ENTRY)); if (!HistoryEntry) { DPRINT1("Failed to allocate memory\n"); Status = STATUS_NO_MEMORY; _SEH2_LEAVE; } HistoryEntry->Target.Buffer = RtlAllocateHeap(BaseSrvHeap, HEAP_ZERO_MEMORY, LinkTarget.Length); if (!HistoryEntry->Target.Buffer) { DPRINT1("Failed to allocate memory\n"); Status = STATUS_NO_MEMORY; _SEH2_LEAVE; } HistoryEntry->Target.Length = HistoryEntry->Target.MaximumLength = LinkTarget.Length; RtlCopyUnicodeString(&HistoryEntry->Target, &LinkTarget); HistoryEntry->Device.Buffer = RtlAllocateHeap(BaseSrvHeap, HEAP_ZERO_MEMORY, RequestDeviceName.Length); if (!HistoryEntry->Device.Buffer) { DPRINT1("Failed to allocate memory\n"); Status = STATUS_NO_MEMORY; _SEH2_LEAVE; } HistoryEntry->Device.Length = HistoryEntry->Device.MaximumLength = RequestDeviceName.Length; RtlCopyUnicodeString(&HistoryEntry->Device, &RequestDeviceName); /* Remember previous symlink target for this device */ InsertHeadList(ListHead, &HistoryEntry->Entry); HistoryEntry = NULL; } RtlAllocateAndInitializeSid(&WorldAuthority, 1, SECURITY_WORLD_RID, SECURITY_NULL_RID, SECURITY_NULL_RID, SECURITY_NULL_RID, SECURITY_NULL_RID, SECURITY_NULL_RID, SECURITY_NULL_RID, SECURITY_NULL_RID, &WorldSid); RtlAllocateAndInitializeSid(&SystemAuthority, 1, SECURITY_LOCAL_SYSTEM_RID, SECURITY_NULL_RID, SECURITY_NULL_RID, SECURITY_NULL_RID, SECURITY_NULL_RID, SECURITY_NULL_RID, SECURITY_NULL_RID, SECURITY_NULL_RID, &SystemSid); RtlAllocateAndInitializeSid(&SystemAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, SECURITY_NULL_RID, SECURITY_NULL_RID, SECURITY_NULL_RID, SECURITY_NULL_RID, SECURITY_NULL_RID, SECURITY_NULL_RID, &AdminSid); SidLength = RtlLengthSid(SystemSid) + RtlLengthSid(AdminSid) + RtlLengthSid(WorldSid); Length = sizeof(ACL) + SidLength + 3 * sizeof(ACCESS_ALLOWED_ACE); SecurityDescriptor = RtlAllocateHeap(BaseSrvHeap, 0, SECURITY_DESCRIPTOR_MIN_LENGTH + Length); if (!SecurityDescriptor) { DPRINT1("Failed to allocate memory\n"); Status = STATUS_NO_MEMORY; _SEH2_LEAVE; } Dacl = (PACL)((ULONG_PTR)SecurityDescriptor + SECURITY_DESCRIPTOR_MIN_LENGTH); Status = RtlCreateSecurityDescriptor(SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION); if (!NT_SUCCESS(Status)) { DPRINT1("RtlCreateSecurityDescriptor() failed (Status %lx)\n", Status); _SEH2_LEAVE; } Status = RtlCreateAcl(Dacl, Length, ACL_REVISION); if (!NT_SUCCESS(Status)) { DPRINT1("RtlCreateAcl() failed (Status %lx)\n", Status); _SEH2_LEAVE; } RtlAddAccessAllowedAce(Dacl, ACL_REVISION, GENERIC_ALL, SystemSid); RtlAddAccessAllowedAce(Dacl, ACL_REVISION, GENERIC_ALL, AdminSid); RtlAddAccessAllowedAce(Dacl, ACL_REVISION, STANDARD_RIGHTS_READ, WorldSid); Status = RtlSetDaclSecurityDescriptor(SecurityDescriptor, TRUE, Dacl, FALSE); if (!NT_SUCCESS(Status)) { DPRINT1("RtlSetDaclSecurityDescriptor() failed (Status %lx)\n", Status); _SEH2_LEAVE; } InitializeObjectAttributes(&ObjectAttributes, &DeviceName, OBJ_CASE_INSENSITIVE, NULL, SecurityDescriptor); Status = NtCreateSymbolicLinkObject(&LinkHandle, SYMBOLIC_LINK_ALL_ACCESS, &ObjectAttributes, RequestLinkTarget); if (NT_SUCCESS(Status)) { Status = NtMakePermanentObject(LinkHandle); if (!NT_SUCCESS(Status)) { DPRINT1("NtMakePermanentObject(%wZ) failed (Status %lx)\n", &DeviceName, Status); } } else { DPRINT1("NtCreateSymbolicLinkObject(%wZ) failed (Status %lx)\n", &DeviceName, Status); } } _SEH2_FINALLY { RtlLeaveCriticalSection(&BaseDefineDosDeviceCritSec); if (DeviceName.Buffer) { RtlFreeHeap(BaseSrvHeap, 0, DeviceName.Buffer); } if (LinkTarget.Buffer) { RtlFreeHeap(BaseSrvHeap, 0, LinkTarget.Buffer); } if (SecurityDescriptor) { RtlFreeHeap(BaseSrvHeap, 0, SecurityDescriptor); } if (LinkHandle) NtClose(LinkHandle); if (SystemSid) RtlFreeSid(SystemSid); if (AdminSid) RtlFreeSid(AdminSid); if (WorldSid) RtlFreeSid(WorldSid); RtlFreeUnicodeString(&RequestDeviceName); if (HistoryEntry) { if (HistoryEntry->Target.Buffer) { RtlFreeHeap(BaseSrvHeap, 0, HistoryEntry->Target.Buffer); } if (HistoryEntry->Device.Buffer) { RtlFreeHeap(BaseSrvHeap, 0, HistoryEntry->Device.Buffer); } RtlFreeHeap(BaseSrvHeap, 0, HistoryEntry); } } _SEH2_END DPRINT("BaseSrvDefineDosDevice exit, Status: 0x%x\n", Status); return Status; } /* EOF */