diff --git a/subsystems/win/basesrv/basesrv.h b/subsystems/win/basesrv/basesrv.h index 03ebb9c8e32..67b73a6da69 100644 --- a/subsystems/win/basesrv/basesrv.h +++ b/subsystems/win/basesrv/basesrv.h @@ -23,6 +23,7 @@ #include #include #include +#include /* PSEH for SEH Support */ #include diff --git a/subsystems/win/basesrv/dosdev.c b/subsystems/win/basesrv/dosdev.c index bd803eb3068..f683b46f532 100644 --- a/subsystems/win/basesrv/dosdev.c +++ b/subsystems/win/basesrv/dosdev.c @@ -15,60 +15,135 @@ /* 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) +NTSTATUS +GetCallerLuid(PLUID CallerLuid) +{ + NTSTATUS Status; + HANDLE TokenHandle; + ULONG ReturnLength; + TOKEN_STATISTICS TokenInformation; + + /* We need an output buffer */ + if (CallerLuid == NULL) { - HistoryEntry = (PBASE_DOS_DEVICE_HISTORY_ENTRY) - CONTAINING_RECORD(Entry, - BASE_DOS_DEVICE_HISTORY_ENTRY, - Entry); - Entry = Entry->Flink; + return STATUS_INVALID_PARAMETER; + } - if (HistoryEntry) + /* Open thread token */ + TokenHandle = 0; + ReturnLength = 0; + Status = NtOpenThreadToken(NtCurrentThread(), + READ_CONTROL | TOKEN_QUERY, + FALSE, &TokenHandle); + /* If we fail, open process token */ + if (Status == STATUS_NO_TOKEN) + { + Status = NtOpenProcessToken(NtCurrentProcess(), + READ_CONTROL | TOKEN_QUERY, + &TokenHandle); + } + + /* In case of a success get caller LUID and copy it back */ + if (NT_SUCCESS(Status)) + { + Status = NtQueryInformationToken(TokenHandle, + TokenStatistics, + &TokenInformation, + sizeof(TokenInformation), + &ReturnLength); + if (NT_SUCCESS(Status)) { - if (HistoryEntry->Target.Buffer) - { - RtlFreeHeap(BaseSrvHeap, - 0, - HistoryEntry->Target.Buffer); - } - if (HistoryEntry->Device.Buffer) - { - RtlFreeHeap(BaseSrvHeap, - 0, - HistoryEntry->Device.Buffer); - } - RtlFreeHeap(BaseSrvHeap, - 0, - HistoryEntry); + RtlCopyLuid(CallerLuid, &TokenInformation.AuthenticationId); } } + + /* Close token handle */ + if (TokenHandle != 0) + { + NtClose(TokenHandle); + } + + return Status; +} + +NTSTATUS +IsGlobalSymbolicLink(HANDLE LinkHandle, + PBOOLEAN IsGlobal) +{ + NTSTATUS Status; + DWORD ReturnLength; + UNICODE_STRING GlobalString; + OBJECT_NAME_INFORMATION NameInfo, *PNameInfo; + + /* We need both parameters */ + if (LinkHandle == 0 || IsGlobal == NULL) + { + return STATUS_INVALID_PARAMETER; + } + + PNameInfo = NULL; + _SEH2_TRY + { + /* Query handle information */ + Status = NtQueryObject(LinkHandle, + ObjectNameInformation, + &NameInfo, + 0, + &ReturnLength); + /* Only failure we tolerate is length mismatch */ + if (NT_SUCCESS(Status) || Status == STATUS_INFO_LENGTH_MISMATCH) + { + /* Allocate big enough buffer */ + PNameInfo = RtlAllocateHeap(BaseSrvHeap, 0, ReturnLength); + if (PNameInfo == NULL) + { + Status = STATUS_NO_MEMORY; + _SEH2_LEAVE; + } + + /* Query again handle information */ + Status = NtQueryObject(LinkHandle, + ObjectNameInformation, + PNameInfo, + ReturnLength, + &ReturnLength); + + /* + * If it succeed, check we have Global?? + * If so, return success + */ + if (NT_SUCCESS(Status)) + { + RtlInitUnicodeString(&GlobalString, L"\\GLOBAL??"); + *IsGlobal = RtlPrefixUnicodeString(&GlobalString, &PNameInfo->Name, FALSE); + Status = STATUS_SUCCESS; + } + } + } + _SEH2_FINALLY + { + if (PNameInfo != NULL) + { + RtlFreeHeap(RtlGetProcessHeap(), 0, PNameInfo); + } + } + _SEH2_END; + + return Status; } /* PUBLIC SERVER APIS *********************************************************/ @@ -78,478 +153,608 @@ CSR_API(BaseSrvDefineDosDevice) NTSTATUS Status; PBASE_DEFINE_DOS_DEVICE DefineDosDeviceRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.DefineDosDeviceRequest; OBJECT_ATTRIBUTES ObjectAttributes; - HANDLE LinkHandle = NULL; + HANDLE LinkHandle; 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; + WCHAR Letter; + SHORT AbsLetter; + BOOLEAN DriveLetter = FALSE; + BOOLEAN RemoveDefinition; + BOOLEAN HandleTarget; + BOOLEAN HandleSMB = FALSE; + BOOLEAN IsGlobal = FALSE; + ULONG CchLengthLeft; + ULONG CchLength; + ULONG TargetLength; + PWSTR TargetBuffer; + PWSTR CurrentBuffer; + /* We store them on the stack, they are known in advance */ + union { + SECURITY_DESCRIPTOR SecurityDescriptor; + UCHAR Buffer[20]; + } SecurityDescriptor; + union { + ACL Dacl; + UCHAR Buffer[256]; + } Dacl; + ACCESS_MASK AccessMask; + LUID CallerLuid; + WCHAR * CurrentPtr; + WCHAR CurrentChar; + PWSTR OrigPtr; + PWSTR InterPtr; + BOOLEAN RemoveFound; - 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)) ) +#if 0 + /* FIXME: Check why it fails.... */ + if (!CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&DefineDosDeviceRequest->DeviceName, + DefineDosDeviceRequest->DeviceName.Length, + 1) || + (DefineDosDeviceRequest->DeviceName.Length & 1) != 0 || + !CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&DefineDosDeviceRequest->TargetPath, + (DefineDosDeviceRequest->TargetPath.Length != 0 ? sizeof(UNICODE_NULL) : 0) + DefineDosDeviceRequest->TargetPath.Length, + 1) || + (DefineDosDeviceRequest->TargetPath.Length & 1) != 0) { return STATUS_INVALID_PARAMETER; } +#endif + DPRINT1("BaseSrvDefineDosDevice entered, Flags:%d, DeviceName:%wZ (%d), TargetPath:%wZ (%d)\n", + DefineDosDeviceRequest->Flags, + &DefineDosDeviceRequest->DeviceName, + DefineDosDeviceRequest->DeviceName.Length, + &DefineDosDeviceRequest->TargetPath, + DefineDosDeviceRequest->TargetPath.Length); + + /* + * Allocate a buffer big enough to contain: + * - device name + * - targets + */ + lpBuffer = RtlAllocateHeap(BaseSrvHeap, 0, 0x2000); + if (lpBuffer == NULL) + { + return STATUS_NO_MEMORY; + } + + /* Enter our critical section */ Status = RtlEnterCriticalSection(&BaseDefineDosDeviceCritSec); if (!NT_SUCCESS(Status)) { DPRINT1("RtlEnterCriticalSection() failed (Status %lx)\n", Status); + RtlFreeHeap(BaseSrvHeap, 0, lpBuffer); return Status; } + LinkHandle = 0; + /* Does the caller wants to remove definition? */ + RemoveDefinition = !!(DefineDosDeviceRequest->Flags & DDD_REMOVE_DEFINITION); _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) + /* First of all, check if that's a drive letter device amongst LUID mappings */ + if (BaseStaticServerData->LUIDDeviceMapsEnabled && !(DefineDosDeviceRequest->Flags & DDD_NO_BROADCAST_SYSTEM)) { - DPRINT1("Failed to allocate memory\n"); - Status = STATUS_NO_MEMORY; + if (DefineDosDeviceRequest->DeviceName.Buffer != NULL && + DefineDosDeviceRequest->DeviceName.Length == 2 * sizeof(WCHAR) && + DefineDosDeviceRequest->DeviceName.Buffer[1] == L':') + { + Letter = DefineDosDeviceRequest->DeviceName.Buffer[0]; + + /* Handle both lower cases and upper cases */ + AbsLetter = Letter - L'a'; + if (AbsLetter < 26 && AbsLetter >= 0) + { + Letter = RtlUpcaseUnicodeChar(Letter); + } + + AbsLetter = Letter - L'A'; + if (AbsLetter < 26) + { + /* That's a letter! */ + DriveLetter = TRUE; + } + } + } + + /* We can only broadcast drive letters in case of LUID mappings */ + if (DefineDosDeviceRequest->Flags & DDD_LUID_BROADCAST_DRIVE && + !DriveLetter) + { + Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } - swprintf(lpBuffer, - L"\\??\\%wZ", - &RequestDeviceName); - RtlInitUnicodeString(&DeviceName, - lpBuffer); + /* First usage of our buffer: create device name */ + CchLength = _snwprintf(lpBuffer, 0x1000, L"\\??\\%wZ", &DefineDosDeviceRequest->DeviceName); + CchLengthLeft = 0x1000 - 1 - CchLength; /* UNICODE_NULL */ + CurrentBuffer = lpBuffer + CchLength + 1; /* UNICODE_NULL */ + RtlInitUnicodeString(&DeviceName, lpBuffer); + + /* And prepare to open it */ InitializeObjectAttributes(&ObjectAttributes, &DeviceName, OBJ_CASE_INSENSITIVE, NULL, NULL); + + /* Assume it's OK and has a target to deal with */ + HandleTarget = TRUE; + + /* Move to the client context if the mapping was local */ + if (!CsrImpersonateClient(NULL)) + { + Status = STATUS_BAD_IMPERSONATION_LEVEL; + _SEH2_LEAVE; + } + + /* While impersonating the caller, also get its LUID */ + if (DriveLetter) + { + Status = GetCallerLuid(&CallerLuid); + if (NT_SUCCESS(Status)) + { + HandleSMB = TRUE; + } + } + + /* Now, open the device */ Status = NtOpenSymbolicLinkObject(&LinkHandle, - DELETE | 0x1, + DELETE | SYMBOLIC_LINK_QUERY, &ObjectAttributes); - if (NT_SUCCESS(Status)) + + /* And get back to our context */ + CsrRevertToSelf(); + + /* In case of LUID broadcast, do nothing but return to trigger broadcast */ + if (DefineDosDeviceRequest->Flags & DDD_LUID_BROADCAST_DRIVE) { - 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); - } - + /* Zero handle in case of a failure */ if (!NT_SUCCESS(Status)) { - DPRINT1("NtQuerySymbolicLinkObject(%wZ) failed (Status %lx)\n", - &DeviceName, Status); - _SEH2_LEAVE; + LinkHandle = 0; } - if ((dwFlags & DDD_REMOVE_DEFINITION)) + /* If removal was asked, and no object found: the remval was successful */ + if (RemoveDefinition && Status == STATUS_OBJECT_NAME_NOT_FOUND) { - /* If no target name specified we remove the current symlink target */ - if (RequestLinkTarget->Length == 0) - Matched = TRUE; - else + Status = STATUS_SUCCESS; + } + + /* We're done here, nothing more to do */ + _SEH2_LEAVE; + } + + /* If device was not found */ + if (Status == STATUS_OBJECT_NAME_NOT_FOUND) + { + /* No handle */ + LinkHandle = 0; + + /* If we were asked to remove... */ + if (RemoveDefinition) + { + /* + * If caller asked to pop first entry, nothing specific, + * then, we can consider this as a success + */ + if (DefineDosDeviceRequest->TargetPath.Length == 0) { - if (dwFlags & DDD_EXACT_MATCH_ON_REMOVE) - Matched = !RtlCompareUnicodeString(RequestLinkTarget, - &LinkTarget, - TRUE); - else - Matched = RtlPrefixUnicodeString(RequestLinkTarget, - &LinkTarget, - TRUE); + Status = STATUS_SUCCESS; } - 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); + /* We're done, nothing to change */ _SEH2_LEAVE; } - Status = NtClose(LinkHandle); - LinkHandle = NULL; - if (!NT_SUCCESS(Status)) - { - DPRINT1("NtClose(%wZ) failed (Status %lx)\n", - &DeviceName, Status); - _SEH2_LEAVE; - } - } + /* There's no target to handle */ + HandleTarget = FALSE; - /* 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); - } + /* + * We'll consider, that's a success + * Failing to open the device doesn't prevent + * from creating it later on to create + * the linking. + */ + Status = STATUS_SUCCESS; } else { - DPRINT1("NtCreateSymbolicLinkObject(%wZ) failed (Status %lx)\n", - &DeviceName, Status); + /* Unexpected failure, forward to caller */ + if (!NT_SUCCESS(Status)) + { + _SEH2_LEAVE; + } + + /* If LUID mapping enabled */ + if (BaseStaticServerData->LUIDDeviceMapsEnabled) + { + /* Check if that's global link */ + Status = IsGlobalSymbolicLink(LinkHandle, &IsGlobal); + if (!NT_SUCCESS(Status)) + { + _SEH2_LEAVE; + } + + /* If so, change our device name namespace to GLOBAL?? for link creation */ + if (IsGlobal) + { + CchLength = _snwprintf(lpBuffer, 0x1000, L"\\GLOBAL??\\%wZ", &DefineDosDeviceRequest->DeviceName); + CchLengthLeft = 0x1000 - 1 - CchLength; /* UNICODE_NULL */ + CurrentBuffer = lpBuffer + CchLength + 1; /* UNICODE_NULL */ + + DeviceName.Length = CchLength * sizeof(WCHAR); + DeviceName.MaximumLength = CchLength * sizeof(WCHAR) + sizeof(UNICODE_NULL); + } + } } + + /* If caller provided a target */ + if (DefineDosDeviceRequest->TargetPath.Length != 0) + { + /* Make sure it's null terminated */ + DefineDosDeviceRequest->TargetPath.Buffer[DefineDosDeviceRequest->TargetPath.Length / sizeof(WCHAR)] = UNICODE_NULL; + + /* Compute its size */ + TargetLength = wcslen(DefineDosDeviceRequest->TargetPath.Buffer); + + /* And make sure it fits our buffer */ + if (TargetLength + 1 >= CchLengthLeft) + { + Status = STATUS_INVALID_PARAMETER; + _SEH2_LEAVE; + } + + /* Copy it to our internal buffer */ + RtlMoveMemory(CurrentBuffer, DefineDosDeviceRequest->TargetPath.Buffer, TargetLength * sizeof(WCHAR) + sizeof(UNICODE_NULL)); + TargetBuffer = CurrentBuffer; + + /* Update our buffer status */ + CchLengthLeft -= (TargetLength + 1); + CurrentBuffer += (TargetLength + 1); + } + /* Otherwise, zero everything */ + else + { + TargetBuffer = NULL; + TargetLength = 0; + } + + /* If we opened the device, then, handle its current target */ + if (HandleTarget) + { + /* Query it with our internal buffer */ + LinkTarget.Length = 0; + LinkTarget.MaximumLength = CchLengthLeft * sizeof(WCHAR); + LinkTarget.Buffer = CurrentBuffer; + + Status = NtQuerySymbolicLinkObject(LinkHandle, + &LinkTarget, + &Length); + /* If we overflow, give up */ + if (Length == LinkTarget.MaximumLength) + { + Status = STATUS_BUFFER_OVERFLOW; + } + /* In case of a failure, bye bye */ + if (!NT_SUCCESS(Status)) + { + _SEH2_LEAVE; + } + + /* + * Properly null it for MULTI_SZ if needed + * Always update max length with + * the need size + * This is needed to hand relatively "small" + * strings to Ob and avoid killing ourselves + * on the next query + */ + CchLength = Length / sizeof(WCHAR); + if (CchLength < 2 || + CurrentBuffer[CchLength - 2] != UNICODE_NULL || + CurrentBuffer[CchLength - 1] != UNICODE_NULL) + { + CurrentBuffer[CchLength] = UNICODE_NULL; + LinkTarget.MaximumLength = Length + sizeof(UNICODE_NULL); + } + else + { + LinkTarget.MaximumLength = Length; + } + } + /* There's no target, and we're asked to remove, so null target */ + else if (RemoveDefinition) + { + RtlInitUnicodeString(&LinkTarget, NULL); + } + /* There's a target provided - new device, update buffer */ + else + { + RtlInitUnicodeString(&LinkTarget, &CurrentBuffer[-TargetLength - 1]); + } + + /* + * We no longer need old symlink, just drop it, we'll recreate it now + * with updated target. + * The benefit of it is that if caller asked us to drop last target, then + * the device is removed and not dangling + */ + if (LinkHandle != 0) + { + Status = NtMakeTemporaryObject(LinkHandle); + NtClose(LinkHandle); + LinkHandle = 0; + } + + /* At this point, we must have no failure */ + if (!NT_SUCCESS(Status)) + { + _SEH2_LEAVE; + } + + /* + * If we have to remove definition, let's start to browse our + * target to actually drop it. + */ + if (RemoveDefinition) + { + /* We'll browse our multi sz string */ + RemoveFound = FALSE; + CurrentPtr = LinkTarget.Buffer; + InterPtr = LinkTarget.Buffer; + while (*CurrentPtr != UNICODE_NULL) + { + CchLength = 0; + OrigPtr = CurrentPtr; + /* First, find next string */ + while (TRUE) + { + CurrentChar = *CurrentPtr; + ++CurrentPtr; + + if (CurrentChar == UNICODE_NULL) + { + break; + } + + ++CchLength; + } + + /* This check is a bit tricky, but dead useful: + * If on the previous loop, we found the caller provided target + * in our list, then, we'll move current entry over the found one + * So that, it gets deleted. + * Also, if we don't find caller entry in our entries, then move + * current entry in the string if a previous one got deleted + */ + if (RemoveFound || + ((!(DefineDosDeviceRequest->Flags & DDD_EXACT_MATCH_ON_REMOVE) || + TargetLength != CchLength || _wcsicmp(OrigPtr, TargetBuffer) != 0) && + ((DefineDosDeviceRequest->Flags & DDD_EXACT_MATCH_ON_REMOVE) || + (TargetLength != 0 && _wcsnicmp(OrigPtr, TargetBuffer, TargetLength) != 0)))) + { + if (InterPtr != OrigPtr) + { + RtlMoveMemory(InterPtr, OrigPtr, sizeof(WCHAR) * CchLength + sizeof(UNICODE_NULL)); + } + + InterPtr += (CchLength + 1); + } + else + { + /* Match case! Remember for next loop turn and to delete it */ + RemoveFound = TRUE; + } + } + + /* + * Drop last entry, as required (pop) + * If there was a match previously, everything + * is already moved, so we're just nulling + * the end of the string + * If there was no match, this is the pop + */ + *InterPtr = UNICODE_NULL; + ++InterPtr; + + /* Compute new target length */ + TargetLength = wcslen(LinkTarget.Buffer) * sizeof(WCHAR); + /* + * If it's empty, quit + * Beware, here, we quit with STATUS_SUCCESS, and that's expected! + * In case we dropped last target entry, then, it's empty + * and there's no need to recreate the device we deleted previously + */ + if (TargetLength == 0) + { + _SEH2_LEAVE; + } + + /* Update our target string */ + LinkTarget.Length = TargetLength; + LinkTarget.MaximumLength = (ULONG_PTR)InterPtr - (ULONG_PTR)LinkTarget.Buffer; + } + /* If that's not a removal, just update the target to include new target */ + else if (HandleTarget) + { + LinkTarget.Buffer = LinkTarget.Buffer - TargetLength - 1; + LinkTarget.Length = TargetLength * sizeof(WCHAR); + LinkTarget.MaximumLength += (TargetLength * sizeof(WCHAR) + sizeof(UNICODE_NULL)); + TargetLength *= sizeof(WCHAR); + } + /* No changes */ + else + { + TargetLength = LinkTarget.Length; + } + + /* Make sure we don't create empty symlink */ + if (TargetLength == 0) + { + _SEH2_LEAVE; + } + + /* Initialize our SIDs for symlink ACLs */ + Status = RtlAllocateAndInitializeSid(&WorldAuthority, + 1, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + &WorldSid); + if (!NT_SUCCESS(Status)) + { + _SEH2_LEAVE; + } + + Status = RtlAllocateAndInitializeSid(&SystemAuthority, + 1, + SECURITY_RESTRICTED_CODE_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + &SystemSid); + if (!NT_SUCCESS(Status)) + { + RtlFreeSid(WorldSid); + _SEH2_LEAVE; + } + + /* Initialize our SD (on stack) */ + RtlCreateSecurityDescriptor(&SecurityDescriptor, + SECURITY_DESCRIPTOR_REVISION); + + /* And our ACL (still on stack) */ + RtlCreateAcl(&Dacl.Dacl, sizeof(Dacl), ACL_REVISION); + + /* + * For access mask, if we have no session ID, or if + * protection mode is disabled, make them wide open + */ + if (SessionId == 0 || + (ProtectionMode & 3) == 0) + { + AccessMask = DELETE | SYMBOLIC_LINK_QUERY; + } + else + { + AccessMask = SYMBOLIC_LINK_QUERY; + } + + /* Setup the ACL */ + RtlAddAccessAllowedAce(&Dacl.Dacl, ACL_REVISION2, AccessMask, WorldSid); + RtlAddAccessAllowedAce(&Dacl.Dacl, ACL_REVISION2, AccessMask, SystemSid); + + /* Drop SIDs */ + RtlFreeSid(WorldSid); + RtlFreeSid(SystemSid); + + /* Link DACL to the SD */ + RtlSetDaclSecurityDescriptor(&SecurityDescriptor, TRUE, &Dacl.Dacl, TRUE); + + /* And set it in the OA used for creation */ + ObjectAttributes.SecurityDescriptor = &SecurityDescriptor; + + /* + * If LUID and not global, we need to impersonate the caller + * to make it local. + */ + if (BaseStaticServerData->LUIDDeviceMapsEnabled) + { + if (!IsGlobal) + { + if (!CsrImpersonateClient(NULL)) + { + Status = STATUS_BAD_IMPERSONATION_LEVEL; + _SEH2_LEAVE; + } + } + } + /* The object will be permanent */ + else + { + ObjectAttributes.Attributes |= OBJ_PERMANENT; + } + + /* (Re)Create the symbolic link/device */ + Status = NtCreateSymbolicLinkObject(&LinkHandle, + SYMBOLIC_LINK_ALL_ACCESS, + &ObjectAttributes, + &LinkTarget); + + /* Revert to self if required */ + if (BaseStaticServerData->LUIDDeviceMapsEnabled && !IsGlobal) + { + CsrRevertToSelf(); + } + + /* In case of a success, make object permanent for LUID links */ + if (NT_SUCCESS(Status)) + { + if (BaseStaticServerData->LUIDDeviceMapsEnabled) + { + Status = NtMakePermanentObject(LinkHandle); + } + + /* Close the link */ + NtClose(LinkHandle); + + /* + * Specific failure case here: + * We were asked to remove something + * but we didn't find the something + * (we recreated the symlink hence the fail here!) + * so fail with appropriate status + */ + if (RemoveDefinition && !RemoveFound) + { + Status = STATUS_OBJECT_NAME_NOT_FOUND; + } + } + + /* We closed link, don't double close */ + LinkHandle = 0; } _SEH2_FINALLY { + /* If we need to close the link, do it now */ + if (LinkHandle != 0) + { + NtClose(LinkHandle); + } + + /* Free our internal buffer */ + RtlFreeHeap(BaseSrvHeap, 0, lpBuffer); + + /* Handle SMB */ + if (DriveLetter && Status == STATUS_SUCCESS && HandleSMB) + { + UNIMPLEMENTED; + } + + /* Done! */ 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 + _SEH2_END; - DPRINT("BaseSrvDefineDosDevice exit, Status: 0x%x\n", Status); return Status; }