/* 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;
+ }
+
+ /* 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);
+ }
- if (HistoryEntry)
+ /* 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)
+ 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)
{
- RtlFreeHeap(BaseSrvHeap,
- 0,
- HistoryEntry->Target.Buffer);
+ Status = STATUS_NO_MEMORY;
+ _SEH2_LEAVE;
}
- if (HistoryEntry->Device.Buffer)
+
+ /* 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))
{
- RtlFreeHeap(BaseSrvHeap,
- 0,
- HistoryEntry->Device.Buffer);
+ RtlInitUnicodeString(&GlobalString, L"\\GLOBAL??");
+ *IsGlobal = RtlPrefixUnicodeString(&GlobalString, &PNameInfo->Name, FALSE);
+ Status = STATUS_SUCCESS;
}
- RtlFreeHeap(BaseSrvHeap,
- 0,
- HistoryEntry);
}
}
+ _SEH2_FINALLY
+ {
+ if (PNameInfo != NULL)
+ {
+ RtlFreeHeap(BaseSrvHeap, 0, PNameInfo);
+ }
+ }
+ _SEH2_END;
+
+ return Status;
}
/* PUBLIC SERVER APIS *********************************************************/
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;
+
+#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
- DPRINT("BaseSrvDefineDosDevice entered, Flags:%d, DeviceName:%wZ, TargetPath:%wZ\n",
+ DPRINT("BaseSrvDefineDosDevice entered, Flags:%d, DeviceName:%wZ (%d), TargetPath:%wZ (%d)\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)) )
+ 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_INVALID_PARAMETER;
+ 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;
+ /* First of all, check if that's a drive letter device amongst LUID mappings */
+ if (BaseStaticServerData->LUIDDeviceMapsEnabled && !(DefineDosDeviceRequest->Flags & DDD_NO_BROADCAST_SYSTEM))
+ {
+ 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);
+ }
- RequestLinkTarget = &DefineDosDeviceRequest->TargetPath;
- lpBuffer = (PWSTR)RtlAllocateHeap(BaseSrvHeap,
- HEAP_ZERO_MEMORY,
- RequestDeviceName.MaximumLength + 5 * sizeof(WCHAR));
- if (!lpBuffer)
+ 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)
{
- DPRINT1("Failed to allocate memory\n");
- Status = STATUS_NO_MEMORY;
+ 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)
+ /* Zero handle in case of a failure */
+ if (!NT_SUCCESS(Status))
{
- LinkTarget.Length = 0;
- LinkTarget.MaximumLength = Length;
- LinkTarget.Buffer = (PWSTR)
- RtlAllocateHeap(BaseSrvHeap,
- HEAP_ZERO_MEMORY,
- Length);
- if (!LinkTarget.Buffer)
+ LinkHandle = 0;
+ }
+
+ /* If removal was asked, and no object found: the remval was successful */
+ if (RemoveDefinition && Status == STATUS_OBJECT_NAME_NOT_FOUND)
+ {
+ 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)
{
- DPRINT1("Failed to allocate memory\n");
- Status = STATUS_NO_MEMORY;
- _SEH2_LEAVE;
+ Status = STATUS_SUCCESS;
}
- Status = NtQuerySymbolicLinkObject(LinkHandle,
- &LinkTarget,
- &Length);
+ /* We're done, nothing to change */
+ _SEH2_LEAVE;
}
+ /* There's no target to handle */
+ HandleTarget = FALSE;
+
+ /*
+ * 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
+ {
+ /* Unexpected failure, forward to caller */
if (!NT_SUCCESS(Status))
{
- DPRINT1("NtQuerySymbolicLinkObject(%wZ) failed (Status %lx)\n",
- &DeviceName, Status);
_SEH2_LEAVE;
}
- if ((dwFlags & DDD_REMOVE_DEFINITION))
+ /* If LUID mapping enabled */
+ if (BaseStaticServerData->LUIDDeviceMapsEnabled)
{
- /* 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))
+ /* Check if that's global link */
+ Status = IsGlobalSymbolicLink(LinkHandle, &IsGlobal);
+ if (!NT_SUCCESS(Status))
{
- /* Current symlink target macthed and there is nothing to revert to */
- RequestLinkTarget = NULL;
+ _SEH2_LEAVE;
}
- 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)
+ /* If so, change our device name namespace to GLOBAL?? for link creation */
+ if (IsGlobal)
{
- /*
- * 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;
- }
+ CchLength = _snwprintf(lpBuffer, 0x1000, L"\\GLOBAL??\\%wZ", &DefineDosDeviceRequest->DeviceName);
+ CchLengthLeft = 0x1000 - 1 - CchLength; /* UNICODE_NULL */
+ CurrentBuffer = lpBuffer + CchLength + 1; /* UNICODE_NULL */
- /* Leave existing symlink as is */
- if (!Matched)
- Status = STATUS_OBJECT_NAME_NOT_FOUND;
- else
- Status = STATUS_SUCCESS;
- _SEH2_LEAVE;
+ DeviceName.Length = CchLength * sizeof(WCHAR);
+ DeviceName.MaximumLength = CchLength * sizeof(WCHAR) + sizeof(UNICODE_NULL);
}
}
- else
+ }
+
+ /* 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)
{
- AddHistory = TRUE;
+ Status = STATUS_INVALID_PARAMETER;
+ _SEH2_LEAVE;
}
- Status = NtMakeTemporaryObject(LinkHandle);
+ /* 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))
{
- DPRINT1("NtMakeTemporaryObject(%wZ) failed (Status %lx)\n",
- &DeviceName, Status);
_SEH2_LEAVE;
}
- Status = NtClose(LinkHandle);
- LinkHandle = NULL;
- if (!NT_SUCCESS(Status))
+ /*
+ * 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)
{
- DPRINT1("NtClose(%wZ) failed (Status %lx)\n",
- &DeviceName, Status);
- _SEH2_LEAVE;
+ 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;
+ }
- /* Don't create symlink if we don't have a target */
- if (!RequestLinkTarget || RequestLinkTarget->Length == 0)
+ /* At this point, we must have no failure */
+ if (!NT_SUCCESS(Status))
+ {
_SEH2_LEAVE;
+ }
- if (AddHistory)
+ /*
+ * If we have to remove definition, let's start to browse our
+ * target to actually drop it.
+ */
+ if (RemoveDefinition)
{
- HistoryEntry = (PBASE_DOS_DEVICE_HISTORY_ENTRY)
- RtlAllocateHeap(BaseSrvHeap,
- HEAP_ZERO_MEMORY,
- sizeof(BASE_DOS_DEVICE_HISTORY_ENTRY));
- if (!HistoryEntry)
+ /* We'll browse our multi sz string */
+ RemoveFound = FALSE;
+ CurrentPtr = LinkTarget.Buffer;
+ InterPtr = LinkTarget.Buffer;
+ while (*CurrentPtr != UNICODE_NULL)
{
- DPRINT1("Failed to allocate memory\n");
- Status = STATUS_NO_MEMORY;
- _SEH2_LEAVE;
- }
+ CchLength = 0;
+ OrigPtr = CurrentPtr;
+ /* First, find next string */
+ while (TRUE)
+ {
+ CurrentChar = *CurrentPtr;
+ ++CurrentPtr;
- 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;
+ 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;
+ }
}
- 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)
+
+ /*
+ * 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)
{
- 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)
+ /* 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)
{
- DPRINT1("Failed to allocate memory\n");
- Status = STATUS_NO_MEMORY;
- _SEH2_LEAVE;
+ 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;
}
- Dacl = (PACL)((ULONG_PTR)SecurityDescriptor + SECURITY_DESCRIPTOR_MIN_LENGTH);
- Status = RtlCreateSecurityDescriptor(SecurityDescriptor,
- SECURITY_DESCRIPTOR_REVISION);
- if (!NT_SUCCESS(Status))
+ /* Make sure we don't create empty symlink */
+ if (TargetLength == 0)
{
- DPRINT1("RtlCreateSecurityDescriptor() failed (Status %lx)\n", Status);
_SEH2_LEAVE;
}
- Status = RtlCreateAcl(Dacl,
- Length,
- ACL_REVISION);
+ /* 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))
{
- 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);
+ 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))
{
- DPRINT1("RtlSetDaclSecurityDescriptor() failed (Status %lx)\n", Status);
+ RtlFreeSid(WorldSid);
_SEH2_LEAVE;
}
- InitializeObjectAttributes(&ObjectAttributes,
- &DeviceName,
- OBJ_CASE_INSENSITIVE,
- NULL,
- SecurityDescriptor);
- Status = NtCreateSymbolicLinkObject(&LinkHandle,
- SYMBOLIC_LINK_ALL_ACCESS,
- &ObjectAttributes,
- RequestLinkTarget);
- if (NT_SUCCESS(Status))
+ /* 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)
{
- Status = NtMakePermanentObject(LinkHandle);
- if (!NT_SUCCESS(Status))
- {
- DPRINT1("NtMakePermanentObject(%wZ) failed (Status %lx)\n",
- &DeviceName, Status);
- }
+ AccessMask = DELETE | SYMBOLIC_LINK_QUERY;
}
else
{
- DPRINT1("NtCreateSymbolicLinkObject(%wZ) failed (Status %lx)\n",
- &DeviceName, Status);
- }
- }
- _SEH2_FINALLY
- {
- RtlLeaveCriticalSection(&BaseDefineDosDeviceCritSec);
- if (DeviceName.Buffer)
- {
- RtlFreeHeap(BaseSrvHeap,
- 0,
- DeviceName.Buffer);
+ AccessMask = SYMBOLIC_LINK_QUERY;
}
- if (LinkTarget.Buffer)
+
+ /* 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)
{
- RtlFreeHeap(BaseSrvHeap,
- 0,
- LinkTarget.Buffer);
+ if (!IsGlobal)
+ {
+ if (!CsrImpersonateClient(NULL))
+ {
+ Status = STATUS_BAD_IMPERSONATION_LEVEL;
+ _SEH2_LEAVE;
+ }
+ }
}
- if (SecurityDescriptor)
+ /* The object will be permanent */
+ else
{
- RtlFreeHeap(BaseSrvHeap,
- 0,
- SecurityDescriptor);
+ ObjectAttributes.Attributes |= OBJ_PERMANENT;
}
- if (LinkHandle) NtClose(LinkHandle);
- if (SystemSid) RtlFreeSid(SystemSid);
- if (AdminSid) RtlFreeSid(AdminSid);
- if (WorldSid) RtlFreeSid(WorldSid);
+ /* (Re)Create the symbolic link/device */
+ Status = NtCreateSymbolicLinkObject(&LinkHandle,
+ SYMBOLIC_LINK_ALL_ACCESS,
+ &ObjectAttributes,
+ &LinkTarget);
- RtlFreeUnicodeString(&RequestDeviceName);
+ /* Revert to self if required */
+ if (BaseStaticServerData->LUIDDeviceMapsEnabled && !IsGlobal)
+ {
+ CsrRevertToSelf();
+ }
- if (HistoryEntry)
+ /* In case of a success, make object permanent for LUID links */
+ if (NT_SUCCESS(Status))
{
- if (HistoryEntry->Target.Buffer)
+ if (BaseStaticServerData->LUIDDeviceMapsEnabled)
{
- RtlFreeHeap(BaseSrvHeap,
- 0,
- HistoryEntry->Target.Buffer);
+ Status = NtMakePermanentObject(LinkHandle);
}
- if (HistoryEntry->Device.Buffer)
+
+ /* 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)
{
- RtlFreeHeap(BaseSrvHeap,
- 0,
- HistoryEntry->Device.Buffer);
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
}
- RtlFreeHeap(BaseSrvHeap,
- 0,
- HistoryEntry);
}
+
+ /* 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);
}
- _SEH2_END
+ _SEH2_END;
- DPRINT("BaseSrvDefineDosDevice exit, Status: 0x%x\n", Status);
return Status;
}