From 4864e1b55309116c7d70e51a23a9c7538f9e486a Mon Sep 17 00:00:00 2001 From: Eric Kohl Date: Sat, 24 Nov 2012 23:19:40 +0000 Subject: [PATCH] [LSASRV] - Add new registry API. - Implement LsarEnumerateAccounts. svn path=/trunk/; revision=57764 --- reactos/dll/win32/lsasrv/CMakeLists.txt | 1 + reactos/dll/win32/lsasrv/lsarpc.c | 199 +++++++++++- reactos/dll/win32/lsasrv/lsasrv.h | 58 ++++ reactos/dll/win32/lsasrv/registry.c | 411 ++++++++++++++++++++++++ 4 files changed, 667 insertions(+), 2 deletions(-) create mode 100644 reactos/dll/win32/lsasrv/registry.c diff --git a/reactos/dll/win32/lsasrv/CMakeLists.txt b/reactos/dll/win32/lsasrv/CMakeLists.txt index 71b4d0a0265..a68f0ec1f49 100644 --- a/reactos/dll/win32/lsasrv/CMakeLists.txt +++ b/reactos/dll/win32/lsasrv/CMakeLists.txt @@ -18,6 +18,7 @@ list(APPEND SOURCE lsasrv.c policy.c privileges.c + registry.c security.c lsasrv.rc ${CMAKE_CURRENT_BINARY_DIR}/lsasrv_stubs.c diff --git a/reactos/dll/win32/lsasrv/lsarpc.c b/reactos/dll/win32/lsasrv/lsarpc.c index 4bb17cedbd9..322660c22a4 100644 --- a/reactos/dll/win32/lsasrv/lsarpc.c +++ b/reactos/dll/win32/lsasrv/lsarpc.c @@ -642,8 +642,203 @@ NTSTATUS WINAPI LsarEnumerateAccounts( PLSAPR_ACCOUNT_ENUM_BUFFER EnumerationBuffer, DWORD PreferedMaximumLength) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + LSAPR_ACCOUNT_ENUM_BUFFER EnumBuffer = {0, NULL}; + PLSA_DB_OBJECT PolicyObject = NULL; + WCHAR AccountKeyName[64]; + HANDLE AccountsKeyHandle = NULL; + HANDLE AccountKeyHandle; + HANDLE SidKeyHandle; + ULONG EnumIndex; + ULONG EnumCount; + ULONG RequiredLength; + ULONG DataLength; + ULONG i; + NTSTATUS Status = STATUS_SUCCESS; + + TRACE("(%p %p %p %lu)\n", PolicyHandle, EnumerationContext, + EnumerationBuffer, PreferedMaximumLength); + + if (EnumerationContext == NULL || + EnumerationBuffer == NULL) + return STATUS_INVALID_PARAMETER; + + EnumerationBuffer->EntriesRead = 0; + EnumerationBuffer->Information = NULL; + + /* Validate the PolicyHandle */ + Status = LsapValidateDbObject(PolicyHandle, + LsaDbPolicyObject, + POLICY_VIEW_LOCAL_INFORMATION, + &PolicyObject); + if (!NT_SUCCESS(Status)) + { + ERR("LsapValidateDbObject returned 0x%08lx\n", Status); + return Status; + } + + Status = LsapRegOpenKey(PolicyObject->KeyHandle, + L"Accounts", + KEY_READ, + &AccountsKeyHandle); + if (!NT_SUCCESS(Status)) + return Status; + + EnumIndex = *EnumerationContext; + EnumCount = 0; + RequiredLength = 0; + + while (TRUE) + { + Status = LsapRegEnumerateSubKey(AccountsKeyHandle, + EnumIndex, + 64 * sizeof(WCHAR), + AccountKeyName); + if (!NT_SUCCESS(Status)) + break; + + TRACE("EnumIndex: %lu\n", EnumIndex); + TRACE("Account key name: %S\n", AccountKeyName); + + Status = LsapRegOpenKey(AccountsKeyHandle, + AccountKeyName, + KEY_READ, + &AccountKeyHandle); + TRACE("LsapRegOpenKey returned %08lX\n", Status); + if (NT_SUCCESS(Status)) + { + Status = LsapRegOpenKey(AccountKeyHandle, + L"Sid", + KEY_READ, + &SidKeyHandle); + TRACE("LsapRegOpenKey returned %08lX\n", Status); + if (NT_SUCCESS(Status)) + { + DataLength = 0; + Status = LsapRegQueryValue(SidKeyHandle, + NULL, + NULL, + NULL, + &DataLength); + TRACE("LsapRegQueryValue returned %08lX\n", Status); + if (NT_SUCCESS(Status)) + { + TRACE("Data length: %lu\n", DataLength); + + if ((RequiredLength + DataLength + sizeof(LSAPR_ACCOUNT_INFORMATION)) > PreferedMaximumLength) + break; + + RequiredLength += (DataLength + sizeof(LSAPR_ACCOUNT_INFORMATION)); + EnumCount++; + } + + LsapRegCloseKey(SidKeyHandle); + } + + LsapRegCloseKey(AccountKeyHandle); + } + + EnumIndex++; + } + + TRACE("EnumCount: %lu\n", EnumCount); + TRACE("RequiredLength: %lu\n", RequiredLength); + + EnumBuffer.EntriesRead = EnumCount; + EnumBuffer.Information = midl_user_allocate(EnumCount * sizeof(LSAPR_ACCOUNT_INFORMATION)); + if (EnumBuffer.Information == NULL) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto done; + } + + EnumIndex = *EnumerationContext; + for (i = 0; i < EnumCount; i++, EnumIndex++) + { + Status = LsapRegEnumerateSubKey(AccountsKeyHandle, + EnumIndex, + 64 * sizeof(WCHAR), + AccountKeyName); + if (!NT_SUCCESS(Status)) + break; + + TRACE("EnumIndex: %lu\n", EnumIndex); + TRACE("Account key name: %S\n", AccountKeyName); + + Status = LsapRegOpenKey(AccountsKeyHandle, + AccountKeyName, + KEY_READ, + &AccountKeyHandle); + TRACE("LsapRegOpenKey returned %08lX\n", Status); + if (NT_SUCCESS(Status)) + { + Status = LsapRegOpenKey(AccountKeyHandle, + L"Sid", + KEY_READ, + &SidKeyHandle); + TRACE("LsapRegOpenKey returned %08lX\n", Status); + if (NT_SUCCESS(Status)) + { + DataLength = 0; + Status = LsapRegQueryValue(SidKeyHandle, + NULL, + NULL, + NULL, + &DataLength); + TRACE("LsapRegQueryValue returned %08lX\n", Status); + if (NT_SUCCESS(Status)) + { + EnumBuffer.Information[i].Sid = midl_user_allocate(DataLength); + if (EnumBuffer.Information[i].Sid == NULL) + { + LsapRegCloseKey(AccountKeyHandle); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto done; + } + + Status = LsapRegQueryValue(SidKeyHandle, + NULL, + NULL, + EnumBuffer.Information[i].Sid, + &DataLength); + TRACE("SampRegQueryValue returned %08lX\n", Status); + } + + LsapRegCloseKey(SidKeyHandle); + } + + LsapRegCloseKey(AccountKeyHandle); + + if (!NT_SUCCESS(Status)) + goto done; + } + } + + if (NT_SUCCESS(Status)) + { + *EnumerationContext += EnumCount; + EnumerationBuffer->EntriesRead = EnumBuffer.EntriesRead; + EnumerationBuffer->Information = EnumBuffer.Information; + } + +done: + if (!NT_SUCCESS(Status)) + { + if (EnumBuffer.Information) + { + for (i = 0; i < EnumBuffer.EntriesRead; i++) + { + if (EnumBuffer.Information[i].Sid != NULL) + midl_user_free(EnumBuffer.Information[i].Sid); + } + + midl_user_free(EnumBuffer.Information); + } + } + + if (AccountsKeyHandle != NULL) + LsapRegCloseKey(AccountsKeyHandle); + + return Status; } diff --git a/reactos/dll/win32/lsasrv/lsasrv.h b/reactos/dll/win32/lsasrv/lsasrv.h index 8849448f3f4..f4eba9fd9bb 100644 --- a/reactos/dll/win32/lsasrv/lsasrv.h +++ b/reactos/dll/win32/lsasrv/lsasrv.h @@ -263,6 +263,64 @@ LsarpEnumeratePrivileges(DWORD *EnumerationContext, PLSAPR_PRIVILEGE_ENUM_BUFFER EnumerationBuffer, DWORD PreferedMaximumLength); +/* registry.h */ +NTSTATUS +LsapRegCloseKey(IN HANDLE KeyHandle); + +NTSTATUS +LsapRegCreateKey(IN HANDLE ParentKeyHandle, + IN LPCWSTR KeyName, + IN ACCESS_MASK DesiredAccess, + OUT HANDLE KeyHandle); + +NTSTATUS +LsapRegDeleteKey(IN HANDLE ParentKeyHandle, + IN LPCWSTR KeyName); + +NTSTATUS +LsapRegEnumerateSubKey(IN HANDLE KeyHandle, + IN ULONG Index, + IN ULONG Length, + OUT LPWSTR Buffer); + +NTSTATUS +LsapRegOpenKey(IN HANDLE ParentKeyHandle, + IN LPCWSTR KeyName, + IN ACCESS_MASK DesiredAccess, + OUT HANDLE KeyHandle); + +NTSTATUS +LsapRegQueryKeyInfo(IN HANDLE KeyHandle, + OUT PULONG SubKeyCount, + OUT PULONG ValueCount); + +NTSTATUS +LsapRegDeleteValue(IN HANDLE KeyHandle, + IN LPWSTR ValueName); + +NTSTATUS +LsapRegEnumerateValue(IN HANDLE KeyHandle, + IN ULONG Index, + OUT LPWSTR Name, + IN OUT PULONG NameLength, + OUT PULONG Type OPTIONAL, + OUT PVOID Data OPTIONAL, + IN OUT PULONG DataLength OPTIONAL); + +NTSTATUS +LsapRegQueryValue(IN HANDLE KeyHandle, + IN LPWSTR ValueName, + OUT PULONG Type OPTIONAL, + OUT LPVOID Data OPTIONAL, + IN OUT PULONG DataLength OPTIONAL); + +NTSTATUS +LsapRegSetValue(IN HANDLE KeyHandle, + IN LPWSTR ValueName, + IN ULONG Type, + IN LPVOID Data, + IN ULONG DataLength); + /* security.c */ NTSTATUS LsapCreatePolicySd(PSECURITY_DESCRIPTOR *PolicySd, diff --git a/reactos/dll/win32/lsasrv/registry.c b/reactos/dll/win32/lsasrv/registry.c new file mode 100644 index 00000000000..80c083eb57a --- /dev/null +++ b/reactos/dll/win32/lsasrv/registry.c @@ -0,0 +1,411 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: Security Account Manager (SAM) Server + * FILE: reactos/dll/win32/samsrv/registry.c + * PURPOSE: Registry helper functions + * + * PROGRAMMERS: Eric Kohl + */ + +/* INCLUDES ****************************************************************/ + +#include "lsasrv.h" + +WINE_DEFAULT_DEBUG_CHANNEL(lsasrv); + +/* FUNCTIONS ***************************************************************/ + +static +BOOLEAN +IsStringType(ULONG Type) +{ + return (Type == REG_SZ) || (Type == REG_EXPAND_SZ) || (Type == REG_MULTI_SZ); +} + + +NTSTATUS +LsapRegCloseKey(IN HANDLE KeyHandle) +{ + return NtClose(KeyHandle); +} + + +NTSTATUS +LsapRegCreateKey(IN HANDLE ParentKeyHandle, + IN LPCWSTR KeyName, + IN ACCESS_MASK DesiredAccess, + OUT HANDLE KeyHandle) +{ + OBJECT_ATTRIBUTES ObjectAttributes; + UNICODE_STRING Name; + ULONG Disposition; + + RtlInitUnicodeString(&Name, KeyName); + + InitializeObjectAttributes(&ObjectAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_OPENIF, + ParentKeyHandle, + NULL); + + /* Create the key */ + return ZwCreateKey(KeyHandle, + DesiredAccess, + &ObjectAttributes, + 0, + NULL, + 0, + &Disposition); +} + + +NTSTATUS +LsapRegDeleteKey(IN HANDLE ParentKeyHandle, + IN LPCWSTR KeyName) +{ + OBJECT_ATTRIBUTES ObjectAttributes; + UNICODE_STRING SubKeyName; + HANDLE TargetKey; + NTSTATUS Status; + + RtlInitUnicodeString(&SubKeyName, + (LPWSTR)KeyName); + InitializeObjectAttributes(&ObjectAttributes, + &SubKeyName, + OBJ_CASE_INSENSITIVE, + ParentKeyHandle, + NULL); + Status = NtOpenKey(&TargetKey, + DELETE, + &ObjectAttributes); + if (!NT_SUCCESS(Status)) + return Status; + + Status = NtDeleteKey(TargetKey); + + NtClose(TargetKey); + + return Status; +} + + +NTSTATUS +LsapRegEnumerateSubKey(IN HANDLE KeyHandle, + IN ULONG Index, + IN ULONG Length, + OUT LPWSTR Buffer) +{ + PKEY_BASIC_INFORMATION KeyInfo = NULL; + ULONG BufferLength = 0; + ULONG ReturnedLength; + NTSTATUS Status; + + /* Check if we have a name */ + if (Length) + { + /* Allocate a buffer for it */ + BufferLength = sizeof(KEY_BASIC_INFORMATION) + Length * sizeof(WCHAR); + + KeyInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength); + if (KeyInfo == NULL) + return STATUS_NO_MEMORY; + } + + /* Enumerate the key */ + Status = ZwEnumerateKey(KeyHandle, + Index, + KeyBasicInformation, + KeyInfo, + BufferLength, + &ReturnedLength); + if (NT_SUCCESS(Status)) + { + /* Check if the name fits */ + if (KeyInfo->NameLength < (Length * sizeof(WCHAR))) + { + /* Copy it */ + RtlMoveMemory(Buffer, + KeyInfo->Name, + KeyInfo->NameLength); + + /* Terminate the string */ + Buffer[KeyInfo->NameLength / sizeof(WCHAR)] = 0; + } + else + { + /* Otherwise, we ran out of buffer space */ + Status = STATUS_BUFFER_OVERFLOW; + } + } + + /* Free the buffer and return status */ + if (KeyInfo) + RtlFreeHeap(RtlGetProcessHeap(), 0, KeyInfo); + + return Status; +} + + +NTSTATUS +LsapRegOpenKey(IN HANDLE ParentKeyHandle, + IN LPCWSTR KeyName, + IN ACCESS_MASK DesiredAccess, + OUT HANDLE KeyHandle) +{ + OBJECT_ATTRIBUTES ObjectAttributes; + UNICODE_STRING Name; + + RtlInitUnicodeString(&Name, KeyName); + + InitializeObjectAttributes(&ObjectAttributes, + &Name, + OBJ_CASE_INSENSITIVE, + ParentKeyHandle, + NULL); + + return NtOpenKey(KeyHandle, + DesiredAccess, + &ObjectAttributes); +} + + +NTSTATUS +LsapRegQueryKeyInfo(IN HANDLE KeyHandle, + OUT PULONG SubKeyCount, + OUT PULONG ValueCount) +{ + KEY_FULL_INFORMATION FullInfoBuffer; + ULONG Length; + NTSTATUS Status; + + FullInfoBuffer.ClassLength = 0; + FullInfoBuffer.ClassOffset = FIELD_OFFSET(KEY_FULL_INFORMATION, Class); + + Status = NtQueryKey(KeyHandle, + KeyFullInformation, + &FullInfoBuffer, + sizeof(KEY_FULL_INFORMATION), + &Length); + TRACE("NtQueryKey() returned status 0x%08lX\n", Status); + if (!NT_SUCCESS(Status)) + return Status; + + if (SubKeyCount != NULL) + *SubKeyCount = FullInfoBuffer.SubKeys; + + if (ValueCount != NULL) + *ValueCount = FullInfoBuffer.Values; + + return Status; +} + + +NTSTATUS +LsapRegDeleteValue(IN HANDLE KeyHandle, + IN LPWSTR ValueName) +{ + UNICODE_STRING Name; + + RtlInitUnicodeString(&Name, + ValueName); + + return NtDeleteValueKey(KeyHandle, + &Name); +} + + +NTSTATUS +LsapRegEnumerateValue(IN HANDLE KeyHandle, + IN ULONG Index, + OUT LPWSTR Name, + IN OUT PULONG NameLength, + OUT PULONG Type OPTIONAL, + OUT PVOID Data OPTIONAL, + IN OUT PULONG DataLength OPTIONAL) +{ + PKEY_VALUE_FULL_INFORMATION ValueInfo = NULL; + ULONG BufferLength = 0; + ULONG ReturnedLength; + NTSTATUS Status; + + TRACE("Index: %lu\n", Index); + + /* Calculate the required buffer length */ + BufferLength = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name); + BufferLength += (MAX_PATH + 1) * sizeof(WCHAR); + if (Data != NULL) + BufferLength += *DataLength; + + /* Allocate the value buffer */ + ValueInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength); + if (ValueInfo == NULL) + return STATUS_NO_MEMORY; + + /* Enumerate the value*/ + Status = ZwEnumerateValueKey(KeyHandle, + Index, + KeyValueFullInformation, + ValueInfo, + BufferLength, + &ReturnedLength); + if (NT_SUCCESS(Status)) + { + if (Name != NULL) + { + /* Check if the name fits */ + if (ValueInfo->NameLength < (*NameLength * sizeof(WCHAR))) + { + /* Copy it */ + RtlMoveMemory(Name, + ValueInfo->Name, + ValueInfo->NameLength); + + /* Terminate the string */ + Name[ValueInfo->NameLength / sizeof(WCHAR)] = 0; + } + else + { + /* Otherwise, we ran out of buffer space */ + Status = STATUS_BUFFER_OVERFLOW; + goto done; + } + } + + if (Data != NULL) + { + /* Check if the data fits */ + if (ValueInfo->DataLength <= *DataLength) + { + /* Copy it */ + RtlMoveMemory(Data, + (PVOID)((ULONG_PTR)ValueInfo + ValueInfo->DataOffset), + ValueInfo->DataLength); + + /* if the type is REG_SZ and data is not 0-terminated + * and there is enough space in the buffer NT appends a \0 */ + if (IsStringType(ValueInfo->Type) && + ValueInfo->DataLength <= *DataLength - sizeof(WCHAR)) + { + WCHAR *ptr = (WCHAR *)((ULONG_PTR)Data + ValueInfo->DataLength); + if ((ptr > (WCHAR *)Data) && ptr[-1]) + *ptr = 0; + } + } + else + { + Status = STATUS_BUFFER_OVERFLOW; + goto done; + } + } + } + +done: + if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_OVERFLOW)) + { + if (Type != NULL) + *Type = ValueInfo->Type; + + if (NameLength != NULL) + *NameLength = ValueInfo->NameLength; + + if (DataLength != NULL) + *DataLength = ValueInfo->DataLength; + } + + /* Free the buffer and return status */ + if (ValueInfo) + RtlFreeHeap(RtlGetProcessHeap(), 0, ValueInfo); + + return Status; +} + + +NTSTATUS +LsapRegQueryValue(IN HANDLE KeyHandle, + IN LPWSTR ValueName, + OUT PULONG Type OPTIONAL, + OUT PVOID Data OPTIONAL, + IN OUT PULONG DataLength OPTIONAL) +{ + PKEY_VALUE_PARTIAL_INFORMATION ValueInfo; + UNICODE_STRING Name; + ULONG BufferLength = 0; + NTSTATUS Status; + + RtlInitUnicodeString(&Name, + ValueName); + + if (DataLength != NULL) + BufferLength = *DataLength; + + BufferLength += FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data); + + /* Allocate memory for the value */ + ValueInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength); + if (ValueInfo == NULL) + return STATUS_NO_MEMORY; + + /* Query the value */ + Status = ZwQueryValueKey(KeyHandle, + &Name, + KeyValuePartialInformation, + ValueInfo, + BufferLength, + &BufferLength); + if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_OVERFLOW)) + { + if (Type != NULL) + *Type = ValueInfo->Type; + + if (DataLength != NULL) + *DataLength = ValueInfo->DataLength; + } + + /* Check if the caller wanted data back, and we got it */ + if ((NT_SUCCESS(Status)) && (Data != NULL)) + { + /* Copy it */ + RtlMoveMemory(Data, + ValueInfo->Data, + ValueInfo->DataLength); + + /* if the type is REG_SZ and data is not 0-terminated + * and there is enough space in the buffer NT appends a \0 */ + if (IsStringType(ValueInfo->Type) && + ValueInfo->DataLength <= *DataLength - sizeof(WCHAR)) + { + WCHAR *ptr = (WCHAR *)((ULONG_PTR)Data + ValueInfo->DataLength); + if ((ptr > (WCHAR *)Data) && ptr[-1]) + *ptr = 0; + } + } + + /* Free the memory and return status */ + RtlFreeHeap(RtlGetProcessHeap(), 0, ValueInfo); + + if ((Data == NULL) && (Status == STATUS_BUFFER_OVERFLOW)) + Status = STATUS_SUCCESS; + + return Status; +} + + +NTSTATUS +LsapRegSetValue(HANDLE KeyHandle, + LPWSTR ValueName, + ULONG Type, + LPVOID Data, + ULONG DataLength) +{ + UNICODE_STRING Name; + + RtlInitUnicodeString(&Name, + ValueName); + + return ZwSetValueKey(KeyHandle, + &Name, + 0, + Type, + Data, + DataLength); +}