reactos/subsystems/win/basesrv/nls.c
Hermès Bélusca-Maïto d8cc88ca80
[KERNEL32][BASESRV] Fix interoperability with Win2k3 regarding NLS section security. (#4828)
Partially revert some aspects of commits 5696e4ba4 and bf40c7a31.
(See PR #4340.)

In order for Win2k3 kernel32.dll to operate with our basesrv.dll (or our
kernel32.dll to operate with Win2k3 basesrv.dll), we need in particular
to have the CreateNlsSecurityDescriptor() helper to exactly take the
expected parameters. Namely, a pointer to a **user-allocated**
SECURITY_DESCRIPTOR buffer, its size (and an access mask).

The function expects its caller to provide all this, and the caller expects
the function to initialize the security descriptor buffer. Note that the
function does *NOT* allocate a new descriptor buffer to be returned!

Indeed, with the way it currently is in master, using Win2k3 kernel32
with our basesrv is now failing with the errors:
```
NLSAPI: Could NOT Create ACL - c0000023.
(subsystems/win/basesrv/nls.c:279) NLS: CreateNlsSecurityDescriptor FAILED!: c0000023
NLSAPI: Could NOT initialize Server - c0000023.
(dll/ntdll/ldr/ldrinit.c:867) LDR: DLL_PROCESS_ATTACH for dll "kernel32.dll" (InitRoutine: 77E40D95) failed
```
(and, if we ever attempted to increase the so-claimed "dummy parameter"
descriptor size in the basesrv call, we would end up with its stack
corrupted and a crash).
Conversely, using our kernel32 with Win2k3 basesrv, would end up with
basesrv receiving a wrongly-initialized descriptor that would not work
(the buffer not being initialized with the contents of a descriptor, but
instead receiving some address to a descriptor allocated somewhere else).
2022-11-01 02:34:04 +01:00

389 lines
13 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Base API Server DLL
* FILE: subsystems/win/basesrv/nls.c
* PURPOSE: National Language Support (NLS)
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
*/
/* INCLUDES *******************************************************************/
#include "basesrv.h"
#include <ndk/mmfuncs.h>
#define NDEBUG
#include <debug.h>
/* GLOBALS ********************************************************************/
RTL_CRITICAL_SECTION NlsCacheCriticalSection;
PNLS_USER_INFO pNlsRegUserInfo;
BOOLEAN BaseSrvKernel32DelayLoadComplete;
HANDLE BaseSrvKernel32DllHandle;
UNICODE_STRING BaseSrvKernel32DllPath;
POPEN_DATA_FILE pOpenDataFile;
PVOID /*PGET_DEFAULT_SORTKEY_SIZE */ pGetDefaultSortkeySize;
PVOID /*PGET_LINGUIST_LANG_SIZE*/ pGetLinguistLangSize;
PVOID /*PNLS_CONVERT_INTEGER_TO_STRING*/ pNlsConvertIntegerToString;
PVOID /*PVALIDATE_LCTYPE*/ pValidateLCType;
PVALIDATE_LOCALE pValidateLocale;
PGET_NLS_SECTION_NAME pGetNlsSectionName;
PVOID /*PGET_USER_DEFAULT_LANGID*/ pGetUserDefaultLangID;
PGET_CP_FILE_NAME_FROM_REGISTRY pGetCPFileNameFromRegistry;
NTSTATUS
(WINAPI *pCreateNlsSecurityDescriptor)(
_Out_ PSECURITY_DESCRIPTOR SecurityDescriptor,
_In_ SIZE_T DescriptorSize,
_In_ ULONG AccessMask);
BASESRV_KERNEL_IMPORTS BaseSrvKernel32Imports[10] =
{
{ "OpenDataFile", (PVOID*) &pOpenDataFile },
{ "GetDefaultSortkeySize", (PVOID*) &pGetDefaultSortkeySize },
{ "GetLinguistLangSize", (PVOID*) &pGetLinguistLangSize },
{ "NlsConvertIntegerToString", (PVOID*) &pNlsConvertIntegerToString },
{ "ValidateLCType", (PVOID*) &pValidateLCType },
{ "ValidateLocale", (PVOID*) &pValidateLocale },
{ "GetNlsSectionName", (PVOID*) &pGetNlsSectionName },
{ "GetUserDefaultLangID", (PVOID*) &pGetUserDefaultLangID },
{ "GetCPFileNameFromRegistry", (PVOID*) &pGetCPFileNameFromRegistry },
{ "CreateNlsSecurityDescriptor", (PVOID*) &pCreateNlsSecurityDescriptor },
};
/* FUNCTIONS *****************************************************************/
NTSTATUS
NTAPI
BaseSrvDelayLoadKernel32(VOID)
{
NTSTATUS Status;
ULONG i;
ANSI_STRING ProcedureName;
/* Only do this once */
if (BaseSrvKernel32DelayLoadComplete) return STATUS_SUCCESS;
/* Loop all imports */
for (i = 0; i < RTL_NUMBER_OF(BaseSrvKernel32Imports); i++)
{
/* Only look them up once */
if (!*BaseSrvKernel32Imports[i].FunctionPointer)
{
/* If we haven't loaded the DLL yet, do it now */
if (!BaseSrvKernel32DllHandle)
{
Status = LdrLoadDll(0,
0,
&BaseSrvKernel32DllPath,
&BaseSrvKernel32DllHandle);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to load %wZ\n", &BaseSrvKernel32DllPath);
return Status;
}
}
/* Get the address of the routine being looked up*/
RtlInitAnsiString(&ProcedureName, BaseSrvKernel32Imports[i].FunctionName);
Status = LdrGetProcedureAddress(BaseSrvKernel32DllHandle,
&ProcedureName,
0,
BaseSrvKernel32Imports[i].FunctionPointer);
DPRINT1("NLS: Found %Z at 0x%p\n",
&ProcedureName,
BaseSrvKernel32Imports[i].FunctionPointer);
if (!NT_SUCCESS(Status)) break;
}
}
/* Did we find them all? */
if (i == RTL_NUMBER_OF(BaseSrvKernel32Imports))
{
/* Excellent */
BaseSrvKernel32DelayLoadComplete = TRUE;
return STATUS_SUCCESS;
}
/* Nope, fail */
return Status;
}
VOID
NTAPI
BaseSrvNLSInit(IN PBASE_STATIC_SERVER_DATA StaticData)
{
/* Initialize the lock */
RtlInitializeCriticalSection(&NlsCacheCriticalSection);
/* Initialize the data with all F's */
pNlsRegUserInfo = &StaticData->NlsUserInfo;
RtlFillMemory(&StaticData->NlsUserInfo, sizeof(StaticData->NlsUserInfo), 0xFF);
/* Set empty LCID */
pNlsRegUserInfo->UserLocaleId = 0;
/* Reset the cache update counter */
RtlEnterCriticalSection(&NlsCacheCriticalSection);
pNlsRegUserInfo->ulCacheUpdateCount = 0;
RtlLeaveCriticalSection(&NlsCacheCriticalSection);
/* Get the LCID */
NtQueryDefaultLocale(0, &pNlsRegUserInfo->UserLocaleId);
}
NTSTATUS
NTAPI
BaseSrvNlsConnect(IN PCSR_PROCESS CsrProcess,
IN OUT PVOID ConnectionInfo,
IN OUT PULONG ConnectionInfoLength)
{
/* Does nothing */
return STATUS_SUCCESS;
}
/* PUBLIC SERVER APIS *********************************************************/
CSR_API(BaseSrvNlsSetUserInfo)
{
DPRINT1("%s not yet implemented\n", __FUNCTION__);
return STATUS_NOT_IMPLEMENTED;
}
CSR_API(BaseSrvNlsSetMultipleUserInfo)
{
DPRINT1("%s not yet implemented\n", __FUNCTION__);
return STATUS_NOT_IMPLEMENTED;
}
CSR_API(BaseSrvNlsCreateSection)
{
NTSTATUS Status;
HANDLE SectionHandle, ProcessHandle, FileHandle;
ULONG LocaleId;
UNICODE_STRING NlsSectionName;
PWCHAR NlsFileName;
UCHAR SecurityDescriptor[NLS_SECTION_SECURITY_DESCRIPTOR_SIZE];
OBJECT_ATTRIBUTES ObjectAttributes;
WCHAR FileNameBuffer[32];
WCHAR NlsSectionNameBuffer[32];
PBASE_NLS_CREATE_SECTION NlsMsg = &((PBASE_API_MESSAGE)ApiMessage)->Data.NlsCreateSection;
/* Load kernel32 first and import the NLS routines */
Status = BaseSrvDelayLoadKernel32();
if (!NT_SUCCESS(Status)) return Status;
/* Assume failure */
NlsMsg->SectionHandle = NULL;
/* Check and validate the locale ID, if one is present */
LocaleId = NlsMsg->LocaleId;
DPRINT1("NLS: Create Section with LCID: %lx for Type: %d\n", LocaleId, NlsMsg->Type);
if (LocaleId)
{
if (!pValidateLocale(LocaleId)) return STATUS_INVALID_PARAMETER;
}
/* Check which NLS section is being created */
switch (NlsMsg->Type)
{
/* For each one, set the correct filename and object name */
case 1:
RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionUnicode");
NlsFileName = L"unicode.nls";
break;
case 2:
RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionLocale");
NlsFileName = L"locale.nls";
break;
case 3:
RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionCType");
NlsFileName = L"ctype.nls";
break;
case 4:
RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionSortkey");
NlsFileName = L"sortkey.nls";
break;
case 5:
RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionSortTbls");
NlsFileName = L"sorttbls.nls";
break;
case 6:
RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionCP437");
NlsFileName = L"c_437.nls";
break;
case 7:
RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionCP1252");
NlsFileName = L"c_1252.nls";
break;
case 8:
RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionLANG_EXCEPT");
NlsFileName = L"l_except.nls";
break;
case 9:
DPRINT1("This type not yet supported\n");
return STATUS_NOT_IMPLEMENTED;
case 10:
DPRINT1("This type not yet supported\n");
return STATUS_NOT_IMPLEMENTED;
case 11:
/* Get the filename for this locale */
if (!pGetCPFileNameFromRegistry(NlsMsg->LocaleId,
FileNameBuffer,
RTL_NUMBER_OF(FileNameBuffer)))
{
DPRINT1("File name query failed\n");
return STATUS_INVALID_PARAMETER;
}
/* Get the name of the section for this locale */
DPRINT1("File name: %S\n", FileNameBuffer);
Status = pGetNlsSectionName(NlsMsg->LocaleId,
10,
0,
L"\\NLS\\NlsSectionCP",
NlsSectionNameBuffer,
RTL_NUMBER_OF(NlsSectionNameBuffer));
if (!NT_SUCCESS(Status))
{
DPRINT1("Section name query failed: %lx\n", Status);
return Status;
}
/* Setup the name and go open it */
NlsFileName = FileNameBuffer;
DPRINT1("Section name: %S\n", NlsSectionNameBuffer);
RtlInitUnicodeString(&NlsSectionName, NlsSectionNameBuffer);
break;
case 12:
RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionGeo");
NlsFileName = L"geo.nls";
break;
default:
DPRINT1("NLS: Invalid NLS type!\n");
return STATUS_INVALID_PARAMETER;
}
/* Open the specified NLS file */
Status = pOpenDataFile(&FileHandle, NlsFileName);
if (Status != STATUS_SUCCESS)
{
DPRINT1("NLS: Failed to open file: %lx\n", Status);
return Status;
}
/* Create an SD for the section object */
Status = pCreateNlsSecurityDescriptor(&SecurityDescriptor,
sizeof(SecurityDescriptor),
SECTION_MAP_READ);
if (!NT_SUCCESS(Status))
{
DPRINT1("NLS: CreateNlsSecurityDescriptor FAILED!: %lx\n", Status);
NtClose(FileHandle);
return Status;
}
/* Create the section object proper */
InitializeObjectAttributes(&ObjectAttributes,
&NlsSectionName,
OBJ_CASE_INSENSITIVE | OBJ_PERMANENT | OBJ_OPENIF,
NULL,
&SecurityDescriptor);
Status = NtCreateSection(&SectionHandle,
SECTION_MAP_READ,
&ObjectAttributes,
0,
PAGE_READONLY,
SEC_COMMIT,
FileHandle);
NtClose(FileHandle);
if (!NT_SUCCESS(Status))
{
DPRINT1("NLS: Failed to create section! %lx\n", Status);
return Status;
}
/* Open a handle to the calling process */
InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
Status = NtOpenProcess(&ProcessHandle,
PROCESS_DUP_HANDLE,
&ObjectAttributes,
&ApiMessage->Header.ClientId);
if (!NT_SUCCESS(Status))
{
DPRINT1("NLS: Failed to open process! %lx\n", Status);
NtClose(SectionHandle);
return Status;
}
/* Duplicate the handle to the section object into it */
Status = NtDuplicateObject(NtCurrentProcess(),
SectionHandle,
ProcessHandle,
&NlsMsg->SectionHandle,
0,
0,
3);
NtClose(ProcessHandle);
return Status;
}
CSR_API(BaseSrvNlsUpdateCacheCount)
{
DPRINT1("%s not yet implemented\n", __FUNCTION__);
return STATUS_NOT_IMPLEMENTED;
}
CSR_API(BaseSrvNlsGetUserInfo)
{
NTSTATUS Status;
PBASE_NLS_GET_USER_INFO NlsMsg = &((PBASE_API_MESSAGE)ApiMessage)->Data.NlsGetUserInfo;
/* Make sure the buffer is valid and of the right size */
if ((CsrValidateMessageBuffer(ApiMessage, &NlsMsg->NlsUserInfo, NlsMsg->Size, sizeof(BYTE))) &&
(NlsMsg->Size == sizeof(NLS_USER_INFO)))
{
/* Acquire the lock to prevent updates while we copy */
Status = RtlEnterCriticalSection(&NlsCacheCriticalSection);
if (NT_SUCCESS(Status))
{
/* Do the copy now, then drop the lock */
RtlCopyMemory(NlsMsg->NlsUserInfo, pNlsRegUserInfo, NlsMsg->Size);
DPRINT1("NLS Data copy complete\n");
RtlLeaveCriticalSection(&NlsCacheCriticalSection);
}
}
else
{
/* The data was invalid, bail out */
DPRINT1("NLS: Size of info is invalid: %lx vs %lx\n", NlsMsg->Size, sizeof(NLS_USER_INFO));
Status = STATUS_INVALID_PARAMETER;
}
/* All done */
return Status;
}
/* PUBLIC APIS ****************************************************************/
NTSTATUS
NTAPI
BaseSrvNlsLogon(DWORD Unknown)
{
DPRINT1("%s(%lu) not yet implemented\n", __FUNCTION__, Unknown);
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
NTAPI
BaseSrvNlsUpdateRegistryCache(DWORD Unknown1,
DWORD Unknown2)
{
DPRINT1("%s(%lu, %lu) not yet implemented\n", __FUNCTION__, Unknown1, Unknown2);
return STATUS_NOT_IMPLEMENTED;
}
/* EOF */