securely access buffers in NtOpenDirectoryObject(), NtQueryDirectoryObject() and NtCreateDirectoryObject() Modified: trunk/reactos/ntoskrnl/ob/dirobj.c _____
Modified: trunk/reactos/ntoskrnl/ob/dirobj.c --- trunk/reactos/ntoskrnl/ob/dirobj.c 2005-01-26 05:00:08 UTC (rev 13305) +++ trunk/reactos/ntoskrnl/ob/dirobj.c 2005-01-26 12:47:38 UTC (rev 13306) @@ -47,65 +47,57 @@
IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes) { - PVOID Object; - NTSTATUS Status; - - *DirectoryHandle = 0; + HANDLE hDirectory; + KPROCESSOR_MODE PreviousMode; + NTSTATUS Status = STATUS_SUCCESS;
- Status = ObReferenceObjectByName(ObjectAttributes->ObjectName, - ObjectAttributes->Attributes, - NULL, - DesiredAccess, - ObDirectoryType, - UserMode, - NULL, - &Object); - if (!NT_SUCCESS(Status)) + PreviousMode = ExGetPreviousMode(); + + if(PreviousMode != KernelMode) + { + _SEH_TRY { - return Status; + ProbeForWrite(DirectoryHandle, + sizeof(HANDLE), + sizeof(ULONG)); } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(!NT_SUCCESS(Status)) + { + DPRINT1("NtOpenDirectoryObject failed, Status: 0x%x\n", Status); + return Status; + } + }
- Status = ObCreateHandle(PsGetCurrentProcess(), - Object, - DesiredAccess, - FALSE, - DirectoryHandle); - return STATUS_SUCCESS; + Status = ObOpenObjectByName(ObjectAttributes, + ObDirectoryType, + NULL, + PreviousMode, + DesiredAccess, + NULL, + &hDirectory); + if(NT_SUCCESS(Status)) + { + _SEH_TRY + { + *DirectoryHandle = hDirectory; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + } + + return Status; }
-static NTSTATUS -CopyDirectoryString(PUNICODE_STRING UnsafeTarget, PUNICODE_STRING Source, PUCHAR *Buffer) -{ - UNICODE_STRING Target; - NTSTATUS Status; - WCHAR NullWchar;
- Target.Length = Source->Length; - Target.MaximumLength = (Source->Length + sizeof (WCHAR)); - Target.Buffer = (PWCHAR) *Buffer; - Status = MmCopyToCaller(UnsafeTarget, &Target, sizeof(UNICODE_STRING)); - if (! NT_SUCCESS(Status)) - { - return Status; - } - Status = MmCopyToCaller(*Buffer, Source->Buffer, Source->Length); - if (! NT_SUCCESS(Status)) - { - return Status; - } - *Buffer += Source->Length; - NullWchar = L'\0'; - Status = MmCopyToCaller(*Buffer, &NullWchar, sizeof(WCHAR)); - if (! NT_SUCCESS(Status)) - { - return Status; - } - *Buffer += sizeof(WCHAR); - - return STATUS_SUCCESS; -} - - /********************************************************************** * NAME EXPORTED * NtQueryDirectoryObject @@ -169,202 +161,229 @@ IN ULONG BufferLength, IN BOOLEAN ReturnSingleEntry, IN BOOLEAN RestartScan, - IN OUT PULONG UnsafeContext, - OUT PULONG UnsafeReturnLength OPTIONAL) + IN OUT PULONG Context, + OUT PULONG ReturnLength OPTIONAL) { - PDIRECTORY_OBJECT dir = NULL; - PLIST_ENTRY current_entry = NULL; - PLIST_ENTRY start_entry; - POBJECT_HEADER current = NULL; - NTSTATUS Status = STATUS_SUCCESS; - ULONG DirectoryCount = 0; - ULONG DirectoryIndex = 0; - POBJECT_DIRECTORY_INFORMATION current_odi = (POBJECT_DIRECTORY_INFORMATION) Buffer; - OBJECT_DIRECTORY_INFORMATION ZeroOdi; - PUCHAR FirstFree = (PUCHAR) Buffer; - ULONG Context; - ULONG RequiredSize; - ULONG NewValue; - KIRQL OldLevel; + PDIRECTORY_OBJECT Directory; + KPROCESSOR_MODE PreviousMode; + ULONG SkipEntries = 0; + ULONG NextEntry = 0; + NTSTATUS Status = STATUS_SUCCESS; + + PreviousMode = ExGetPreviousMode();
- DPRINT("NtQueryDirectoryObject(DirectoryHandle %x)\n", DirectoryHandle); - - /* Check Context is not NULL */ - if (NULL == UnsafeContext) + if(PreviousMode != KernelMode) + { + _SEH_TRY + { + /* a test showed that the Buffer pointer just has to be 16 bit aligned, + propably due to the fact that most information that needs to be copied + is unicode strings */ + ProbeForWrite(Buffer, + BufferLength, + sizeof(WCHAR)); + ProbeForWrite(Context, + sizeof(ULONG), + sizeof(ULONG)); + if(!RestartScan) { - return STATUS_INVALID_PARAMETER; + SkipEntries = *Context; } - - /* Reference the DIRECTORY_OBJECT */ - Status = ObReferenceObjectByHandle(DirectoryHandle, - DIRECTORY_QUERY, - ObDirectoryType, - UserMode, - (PVOID*)&dir, - NULL); - if (!NT_SUCCESS(Status)) + if(ReturnLength != NULL) { - return Status; + ProbeForWrite(ReturnLength, + sizeof(ULONG), + sizeof(ULONG)); } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END;
- KeAcquireSpinLock(&dir->Lock, &OldLevel); + if(!NT_SUCCESS(Status)) + { + DPRINT1("NtQueryDirectoryObject failed, Status: 0x%x\n", Status); + return Status; + } + } + else if(!RestartScan) + { + SkipEntries = *Context; + } + + Status = ObReferenceObjectByHandle(DirectoryHandle, + DIRECTORY_QUERY, + ObDirectoryType, + PreviousMode, + (PVOID*)&Directory, + NULL); + if(NT_SUCCESS(Status)) + { + PVOID TemporaryBuffer = ExAllocatePool(PagedPool, + BufferLength); + if(TemporaryBuffer != NULL) + { + POBJECT_HEADER EntryHeader; + PLIST_ENTRY ListEntry; + KIRQL OldLevel; + ULONG RequiredSize = 0; + ULONG nDirectories = 0; + POBJECT_DIRECTORY_INFORMATION DirInfo = (POBJECT_DIRECTORY_INFORMATION)TemporaryBuffer;
- /* - * Optionally, skip over some entries at the start of the directory - * (use *ObjectIndex value) - */ - start_entry = dir->head.Flink; - if (! RestartScan) + KeAcquireSpinLock(&Directory->Lock, &OldLevel); + + for(ListEntry = Directory->head.Flink; + ListEntry != &Directory->head; + ListEntry = ListEntry->Flink) { - register ULONG EntriesToSkip; + NextEntry++; + if(SkipEntries == 0) + { + PUNICODE_STRING Name, Type; + ULONG EntrySize;
- Status = MmCopyFromCaller(&Context, UnsafeContext, sizeof(ULONG)); - if (! NT_SUCCESS(Status)) - { - KeReleaseSpinLock(&dir->Lock, OldLevel); - ObDereferenceObject(dir); - return Status; - } - EntriesToSkip = Context; + EntryHeader = CONTAINING_RECORD(ListEntry, OBJECT_HEADER, Entry);
- CHECKPOINT; - - for (; 0 != EntriesToSkip-- && start_entry != &dir->head; - start_entry = start_entry->Flink) - { - ; - } - if ((0 != EntriesToSkip) && (start_entry == &dir->head)) - { - KeReleaseSpinLock(&dir->Lock, OldLevel); - ObDereferenceObject(dir); - return STATUS_NO_MORE_ENTRIES; - } - } + /* calculate the size of the required buffer space for this entry */ + Name = (EntryHeader->Name.Length != 0 ? &EntryHeader->Name : NULL); + Type = &EntryHeader->ObjectType->TypeName; + EntrySize = sizeof(OBJECT_DIRECTORY_INFORMATION) + + ((Name != NULL) ? ((ULONG)Name->Length + sizeof(WCHAR)) : 0) + + (ULONG)EntryHeader->ObjectType->TypeName.Length + sizeof(WCHAR);
- /* - * Compute number of entries that we will copy into the buffer and - * the total size of all entries (even if larger than the buffer size) - */ - DirectoryCount = 0; - /* For the end sentenil */ - RequiredSize = sizeof(OBJECT_DIRECTORY_INFORMATION); - for (current_entry = start_entry; - current_entry != &dir->head; - current_entry = current_entry->Flink) - { - current = CONTAINING_RECORD(current_entry, OBJECT_HEADER, Entry); + if(RequiredSize + EntrySize <= BufferLength) + { + /* the buffer is large enough to receive this entry. It would've + been much easier if the strings were directly appended to the + OBJECT_DIRECTORY_INFORMATION structured written into the buffer */ + if(Name != NULL) + DirInfo->ObjectName = *Name; + else + { + DirInfo->ObjectName.Length = DirInfo->ObjectName.MaximumLength = 0; + DirInfo->ObjectName.Buffer = NULL; + } + DirInfo->ObjectTypeName = *Type;
- RequiredSize += sizeof(OBJECT_DIRECTORY_INFORMATION) + - current->Name.Length + sizeof(WCHAR) + - current->ObjectType->TypeName.Length + sizeof(WCHAR); - if (RequiredSize <= BufferLength && - (! ReturnSingleEntry || DirectoryCount < 1)) - { - DirectoryCount++; - } - } + nDirectories++; + RequiredSize += EntrySize;
- /* - * If there's no room to even copy a single entry then return error - * status. - */ - if (0 == DirectoryCount && - !(IsListEmpty(&dir->head) && BufferLength >= RequiredSize)) - { - KeReleaseSpinLock(&dir->Lock, OldLevel); - ObDereferenceObject(dir); - if (NULL != UnsafeReturnLength) - { - Status = MmCopyToCaller(UnsafeReturnLength, &RequiredSize, sizeof(ULONG)); - } - return NT_SUCCESS(Status) ? STATUS_BUFFER_TOO_SMALL : Status; + if(ReturnSingleEntry) + { + /* we're only supposed to query one entry, so bail and copy the + strings to the buffer */ + break; + } + DirInfo++; + } + else + { + if(ReturnSingleEntry) + { + /* the buffer is too small, so return the number of bytes that + would've been required for this query */ + RequiredSize += EntrySize; + Status = STATUS_BUFFER_TOO_SMALL; + } + else + { + /* just copy the entries that fit into the buffer */ + Status = STATUS_NO_MORE_ENTRIES; + } + break; + } + } + else + { + /* skip the entry */ + SkipEntries--; + } }
- /* - * Move FirstFree to point to the Unicode strings area - */ - FirstFree += (DirectoryCount + 1) * sizeof(OBJECT_DIRECTORY_INFORMATION); - - /* Scan the directory */ - current_entry = start_entry; - for (DirectoryIndex = 0; DirectoryIndex < DirectoryCount; DirectoryIndex++) + if(NT_SUCCESS(Status)) { - current = CONTAINING_RECORD(current_entry, OBJECT_HEADER, Entry); + if(SkipEntries > 0 || nDirectories == 0) + { + /* we skipped more entries than the directory contains, nothing more to do */ + Status = STATUS_NO_MORE_ENTRIES; + } + else + { + _SEH_TRY + { + POBJECT_DIRECTORY_INFORMATION DestDirInfo = (POBJECT_DIRECTORY_INFORMATION)Buffer; + PWSTR strbuf = (PWSTR)((POBJECT_DIRECTORY_INFORMATION)Buffer + nDirectories);
- /* - * Copy the current directory entry's data into the buffer - * and update the OBJDIR_INFORMATION entry in the array. - */ - /* --- Object's name --- */ - Status = CopyDirectoryString(¤t_odi->ObjectName, ¤t->Name, &FirstFree); - if (! NT_SUCCESS(Status)) - { - KeReleaseSpinLock(&dir->Lock, OldLevel); - ObDereferenceObject(dir); - return Status; - } - /* --- Object type's name --- */ - Status = CopyDirectoryString(¤t_odi->ObjectTypeName, ¤t->ObjectType->TypeName, &FirstFree); - if (! NT_SUCCESS(Status)) - { - KeReleaseSpinLock(&dir->Lock, OldLevel); - ObDereferenceObject(dir); - return Status; - } + /* copy all OBJECT_DIRECTORY_INFORMATION structures to the buffer and + just append all strings (whose pointers are stored in the buffer!) + and replace the pointers */ + for(DirInfo = (POBJECT_DIRECTORY_INFORMATION)TemporaryBuffer; + nDirectories > 0; + nDirectories--, DirInfo++, DestDirInfo++) + { + if(DirInfo->ObjectName.Length > 0) + { + DestDirInfo->ObjectName.Length = DirInfo->ObjectName.Length; + DestDirInfo->ObjectName.MaximumLength = DirInfo->ObjectName.MaximumLength; + DestDirInfo->ObjectName.Buffer = strbuf; + RtlCopyMemory(strbuf, + DirInfo->ObjectName.Buffer, + DirInfo->ObjectName.Length); + /* NULL-terminate the string */ + strbuf[DirInfo->ObjectName.Length / sizeof(WCHAR)] = L'\0'; + strbuf += (DirInfo->ObjectName.Length / sizeof(WCHAR)) + 1; + } + + DestDirInfo->ObjectTypeName.Length = DirInfo->ObjectTypeName.Length; + DestDirInfo->ObjectTypeName.MaximumLength = DirInfo->ObjectTypeName.MaximumLength; + DestDirInfo->ObjectTypeName.Buffer = strbuf; + RtlCopyMemory(strbuf, + DirInfo->ObjectTypeName.Buffer, + DirInfo->ObjectTypeName.Length); + /* NULL-terminate the string */ + strbuf[DirInfo->ObjectTypeName.Length / sizeof(WCHAR)] = L'\0'; + strbuf += (DirInfo->ObjectTypeName.Length / sizeof(WCHAR)) + 1; + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + } + }
- /* Next entry in the array */ - current_odi++; - /* Next object in the directory */ - current_entry = current_entry->Flink; - } + KeReleaseSpinLock(&Directory->Lock, OldLevel); + ObDereferenceObject(Directory); + + ExFreePool(TemporaryBuffer);
- /* - * Don't need dir object anymore - */ - KeReleaseSpinLock(&dir->Lock, OldLevel); - ObDereferenceObject(dir); - - /* Terminate with all zero entry */ - memset(&ZeroOdi, '\0', sizeof(OBJECT_DIRECTORY_INFORMATION)); - Status = MmCopyToCaller(current_odi, &ZeroOdi, sizeof(OBJECT_DIRECTORY_INFORMATION)); - if (! NT_SUCCESS(Status)) + if(NT_SUCCESS(Status) || ReturnSingleEntry) { - return Status; + _SEH_TRY + { + *Context = NextEntry; + if(ReturnLength != NULL) + { + *ReturnLength = RequiredSize; + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; } - - /* - * Store current index in Context - */ - if (RestartScan) - { - Context = DirectoryCount; - } + } else - { - Context += DirectoryCount; - } - Status = MmCopyToCaller(UnsafeContext, &Context, sizeof(ULONG)); - if (! NT_SUCCESS(Status)) - { - return Status; - } - - /* - * Report to the caller how much bytes - * we wrote in the user buffer. - */ - if (NULL != UnsafeReturnLength) - { - NewValue = FirstFree - (PUCHAR) Buffer; - Status = MmCopyToCaller(UnsafeReturnLength, &NewValue, sizeof(ULONG)); - if (! NT_SUCCESS(Status)) - { - return Status; - } - } - - return Status; + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + return Status; }
@@ -396,43 +415,69 @@ IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes) { - PDIRECTORY_OBJECT DirectoryObject; - NTSTATUS Status; - + PDIRECTORY_OBJECT Directory; + HANDLE hDirectory; + KPROCESSOR_MODE PreviousMode; + NTSTATUS Status = STATUS_SUCCESS; + DPRINT("NtCreateDirectoryObject(DirectoryHandle %x, " - "DesiredAccess %x, ObjectAttributes %x, " - "ObjectAttributes->ObjectName %wZ)\n", - DirectoryHandle, DesiredAccess, ObjectAttributes, - ObjectAttributes->ObjectName); + "DesiredAccess %x, ObjectAttributes %x\n", + DirectoryHandle, DesiredAccess, ObjectAttributes);
- Status = NtOpenDirectoryObject (DirectoryHandle, - DesiredAccess, - ObjectAttributes); + PreviousMode = ExGetPreviousMode();
- if (Status == STATUS_OBJECT_NAME_NOT_FOUND) + if(PreviousMode != KernelMode) { - Status = ObCreateObject (ExGetPreviousMode(), - ObDirectoryType, - ObjectAttributes, - ExGetPreviousMode(), - NULL, - sizeof(DIRECTORY_OBJECT), - 0, - 0, - (PVOID*)&DirectoryObject); - if (!NT_SUCCESS(Status)) - { - return Status; - } + _SEH_TRY + { + ProbeForWrite(DirectoryHandle, + sizeof(HANDLE), + sizeof(ULONG)); + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END;
- Status = ObInsertObject ((PVOID)DirectoryObject, - NULL, - DesiredAccess, - 0, - NULL, - DirectoryHandle); + if(!NT_SUCCESS(Status)) + { + DPRINT1("NtCreateDirectoryObject failed, Status: 0x%x\n", Status); + return Status; + } + }
- ObDereferenceObject(DirectoryObject); + Status = ObCreateObject(PreviousMode, + ObDirectoryType, + ObjectAttributes, + PreviousMode, + NULL, + sizeof(DIRECTORY_OBJECT), + 0, + 0, + (PVOID*)&Directory); + if(NT_SUCCESS(Status)) + { + Status = ObInsertObject((PVOID)Directory, + NULL, + DesiredAccess, + 0, + NULL, + &hDirectory); + ObDereferenceObject(Directory); + + if(NT_SUCCESS(Status)) + { + _SEH_TRY + { + *DirectoryHandle = hDirectory; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + } }
return Status;