From: Steven Flint sflint96@proton.me
Windows Vista added the concept of virtual accounts for services. There exists a deterministic mapping between the name of a service and its security identifier.
The format of virtual account's SID is the following. Revision: 1 Identifier Authority: NT_AUTHORITY (5) Domain Identifier: NT_SERVICE (80) Relative Identifier: SHA1(ServiceName)
For example the SID of a service named "TestService" is: S-1-5-80-3892056402-659729507-4115993473-1921682939-1565901394
RtlCreateServiceSid generates the SID of a service from its name. --- dlls/ntdll/ntdll.spec | 1 + dlls/ntdll/sec.c | 60 ++++++++++++++++++++++++++++++++++++++++++ dlls/ntdll/tests/rtl.c | 31 ++++++++++++++++++++++ include/winternl.h | 1 + 4 files changed, 93 insertions(+)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index a51cab8263f..0981ec50566 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -589,6 +589,7 @@ @ stub RtlCreatePropertySet @ stdcall RtlCreateQueryDebugBuffer(long long) @ stdcall RtlCreateRegistryKey(long wstr) +@ stdcall RtlCreateServiceSid(ptr ptr ptr); @ stdcall RtlCreateSecurityDescriptor(ptr long) # @ stub RtlCreateSystemVolumeInformationFolder @ stub RtlCreateTagHeap diff --git a/dlls/ntdll/sec.c b/dlls/ntdll/sec.c index c1b5664cee2..ca3b5964ccb 100644 --- a/dlls/ntdll/sec.c +++ b/dlls/ntdll/sec.c @@ -1870,6 +1870,66 @@ NTSTATUS WINAPI RtlDefaultNpAcl(PACL *pAcl) return STATUS_SUCCESS; }
+/****************************************************************************** + * RtlCreateServiceSid [NTDLL.@] + * + * Generate the SID for a service name. + * + * If pServiceSid is NULL, then pServiceSidLength must be 0 or it will crash. + * + * PARAMS + * pServiceName [I] Name to generate for + * pServiceSid [O] The SID generated + * pServiceSidLength [IO] The length of the SID that generated + * + * RETURNS + * STATUS_SUCCESS on success + * STATUS_INVALID_PARAMETER if pServiceName or pServiceSidLength is NULL + * STATUS_BUFFER_TOO_SMALL if pServiceSidLength is to small + * STATUS_NO_MEMORY if memory runs out + */ +NTSTATUS WINAPI RtlCreateServiceSid( PUNICODE_STRING pServiceName, PSID pServiceSid, LPDWORD pServiceSidLength ) +{ + static const SID_IDENTIFIER_AUTHORITY nt_authority = { SECURITY_NT_AUTHORITY }; + DWORD service_sid_length; + BOOL too_small; + NTSTATUS status; + UNICODE_STRING service_name_upper; + hash_state hash_ctx; + ULONG hash[5]; + + if (pServiceName == NULL || pServiceSidLength == NULL) + { + return STATUS_INVALID_PARAMETER; + } + + service_sid_length = RtlLengthRequiredSid( 1 + ARRAY_SIZE(hash) ); + too_small = *pServiceSidLength < service_sid_length; + *pServiceSidLength = service_sid_length; + if (too_small) + { + return STATUS_BUFFER_TOO_SMALL; + } + + ((SID*)pServiceSid)->Revision = SID_REVISION; + ((SID*)pServiceSid)->IdentifierAuthority = nt_authority; + ((SID*)pServiceSid)->SubAuthorityCount = 1 + ARRAY_SIZE(hash); + ((SID*)pServiceSid)->SubAuthority[0] = SECURITY_SERVICE_ID_BASE_RID; + + if ((status = RtlUpcaseUnicodeString( &service_name_upper, pServiceName, TRUE ))) + { + return status; + } + + sha1_init( &hash_ctx ); + sha1_process( &hash_ctx, (UCHAR *)service_name_upper.Buffer, service_name_upper.Length ); + sha1_done( &hash_ctx, (UCHAR *)hash ); + RtlFreeUnicodeString( &service_name_upper ); + memcpy( ((SID*)pServiceSid)->SubAuthority + 1, hash, sizeof(hash) ); + + return STATUS_SUCCESS; +} + /****************************************************************************** * RtlDeriveCapabilitySidsFromName (NTDLL.@) */ diff --git a/dlls/ntdll/tests/rtl.c b/dlls/ntdll/tests/rtl.c index e3f3efa5bfc..0beefa846e4 100644 --- a/dlls/ntdll/tests/rtl.c +++ b/dlls/ntdll/tests/rtl.c @@ -34,6 +34,7 @@ #include "in6addr.h" #include "inaddr.h" #include "ip2string.h" +#include "sddl.h" #include "ddk/ntifs.h" #include "wine/test.h" #include "wine/asm.h" @@ -127,6 +128,7 @@ static VOID (WINAPI *pRtlGetDeviceFamilyInfoEnum)(ULONGLONG *,DWORD *,DWORD static void (WINAPI *pRtlRbInsertNodeEx)(RTL_RB_TREE *, RTL_BALANCED_NODE *, BOOLEAN, RTL_BALANCED_NODE *); static void (WINAPI *pRtlRbRemoveNode)(RTL_RB_TREE *, RTL_BALANCED_NODE *); static DWORD (WINAPI *pRtlConvertDeviceFamilyInfoToString)(DWORD *, DWORD *, WCHAR *, WCHAR *); +static NTSTATUS (WINAPI *pRtlCreateServiceSid)(PUNICODE_STRING, PSID, PULONG); static NTSTATUS (WINAPI *pRtlDeriveCapabilitySidsFromName)(UNICODE_STRING *, PSID, PSID); static NTSTATUS (WINAPI *pRtlInitializeNtUserPfn)( const UINT64 *client_procsA, ULONG procsA_size, const UINT64 *client_procsW, ULONG procsW_size, @@ -194,6 +196,7 @@ static void InitFunctionPtrs(void) pLdrEnumerateLoadedModules = (void *)GetProcAddress(hntdll, "LdrEnumerateLoadedModules"); pLdrRegisterDllNotification = (void *)GetProcAddress(hntdll, "LdrRegisterDllNotification"); pLdrUnregisterDllNotification = (void *)GetProcAddress(hntdll, "LdrUnregisterDllNotification"); + pRtlCreateServiceSid = (void *)GetProcAddress(hntdll, "RtlCreateServiceSid"); pRtlDeriveCapabilitySidsFromName = (void *)GetProcAddress(hntdll, "RtlDeriveCapabilitySidsFromName"); pRtlGetDeviceFamilyInfoEnum = (void *)GetProcAddress(hntdll, "RtlGetDeviceFamilyInfoEnum"); pRtlRbInsertNodeEx = (void *)GetProcAddress(hntdll, "RtlRbInsertNodeEx"); @@ -5388,6 +5391,33 @@ static void test_RtlGetElementGenericTable(void) } }
+#include "wine/debug.h" + +static void test_RtlCreateServiceSid(void) +{ + UNICODE_STRING service_name; + SID* service_sid; + ULONG service_sid_length = 0; + LPWSTR string_sid; + + if (!pRtlCreateServiceSid) + { + win_skip( "RtlCreateServiceSid is not available.\n" ); + return; + } + + RtlInitUnicodeString( &service_name, L"TestService" ); + ok( pRtlCreateServiceSid(NULL, NULL, &service_sid_length) == STATUS_INVALID_PARAMETER, "NULL pServiceName is invalid.\n" ); + ok( pRtlCreateServiceSid(&service_name, NULL, NULL) == STATUS_INVALID_PARAMETER, "NULL pServiceSidLength is invalid.\n" ); + ok( pRtlCreateServiceSid(&service_name, NULL, &service_sid_length) == STATUS_BUFFER_TOO_SMALL, "SID buffer must be big enough.\n" ); + ok( service_sid_length != 0, "The length should be written if the buffer is too small" ); + service_sid = malloc( service_sid_length ); + ok( pRtlCreateServiceSid(&service_name, service_sid, &service_sid_length) == STATUS_SUCCESS, "The length from the a previous call should be enough.\n" ); + ConvertSidToStringSidW(service_sid, &string_sid); + ok( wcscmp(string_sid, L"S-1-5-80-3892056402-659729507-4115993473-1921682939-1565901394") == 0, "TestService SID is wrong"); + free(service_sid); +} + static void test_RtlDeriveCapabilitySidsFromName(void) { static const SID_IDENTIFIER_AUTHORITY app_authority = { SECURITY_APP_PACKAGE_AUTHORITY }; @@ -5516,5 +5546,6 @@ START_TEST(rtl) test_RtlEnumerateGenericTableWithoutSplaying(); test_RtlEnumerateGenericTable(); test_RtlGetElementGenericTable(); + test_RtlCreateServiceSid(); test_RtlDeriveCapabilitySidsFromName(); } diff --git a/include/winternl.h b/include/winternl.h index 2b1e3d029ed..19d74eba50d 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -4820,6 +4820,7 @@ NTSYSAPI NTSTATUS WINAPI RtlCreateProcessParametersEx(RTL_USER_PROCESS_PARAMETE NTSYSAPI PDEBUG_BUFFER WINAPI RtlCreateQueryDebugBuffer(ULONG,BOOLEAN); NTSYSAPI NTSTATUS WINAPI RtlCreateRegistryKey(ULONG,PWSTR); NTSYSAPI NTSTATUS WINAPI RtlCreateSecurityDescriptor(PSECURITY_DESCRIPTOR,DWORD); +NTSYSAPI NTSTATUS WINAPI RtlCreateServiceSid(PUNICODE_STRING, PSID, PULONG); NTSYSAPI NTSTATUS WINAPI RtlCreateTimer(HANDLE,HANDLE*,RTL_WAITORTIMERCALLBACKFUNC, PVOID, DWORD, DWORD, ULONG); NTSYSAPI NTSTATUS WINAPI RtlCreateTimerQueue(PHANDLE); NTSYSAPI BOOLEAN WINAPI RtlCreateUnicodeString(PUNICODE_STRING,LPCWSTR);