Author: fireball Date: Mon Jan 14 14:46:14 2008 New Revision: 31768
URL: http://svn.reactos.org/svn/reactos?rev=31768&view=rev Log: - Implement canonicalizing the service binary path properly, handling all possible cases (instead of a memory-overwriting hack Christoph noticed yesterday).
Modified: trunk/reactos/base/system/services/rpcserver.c
Modified: trunk/reactos/base/system/services/rpcserver.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/base/system/services/rpcser... ============================================================================== --- trunk/reactos/base/system/services/rpcserver.c (original) +++ trunk/reactos/base/system/services/rpcserver.c Mon Jan 14 14:46:14 2008 @@ -993,6 +993,380 @@ DPRINT("ScmrChangeServiceConfigW() done (Error %lu)\n", dwError);
return dwError; +} + +/* Create a path suitable for the bootloader out of the full path */ +DWORD +ScmConvertToBootPathName(wchar_t *CanonName, wchar_t **RelativeName) +{ + DWORD ServiceNameLen, BufferSize, ExpandedLen; + WCHAR Dest; + WCHAR *Expanded; + UNICODE_STRING NtPathName, SystemRoot, LinkTarget; + OBJECT_ATTRIBUTES ObjectAttributes; + NTSTATUS Status; + HANDLE SymbolicLinkHandle; + + DPRINT("ScmConvertToBootPathName %S\n", CanonName); + + ServiceNameLen = wcslen(CanonName); + /* First check, if it's already good */ + if (ServiceNameLen > 12 && + !wcsnicmp(L"\SystemRoot\", CanonName, 12)) + { + *RelativeName = LocalAlloc(LMEM_ZEROINIT, ServiceNameLen * sizeof(WCHAR) + sizeof(WCHAR)); + + if (*RelativeName == NULL) + { + DPRINT1("Error allocating memory for boot driver name!\n"); + return ERROR_NOT_ENOUGH_MEMORY; + } + + /* Copy it */ + wcscpy(*RelativeName, CanonName); + + DPRINT1("Bootdriver name %S\n", *RelativeName); + return ERROR_SUCCESS; + } + + /* If it has %SystemRoot% prefix, substitute it to \System*/ + if (ServiceNameLen > 13 && + !wcsnicmp(L"%SystemRoot%\", CanonName, 13)) + { + /* There is no +sizeof(wchar_t) because the name is less by 1 wchar */ + *RelativeName = LocalAlloc(LMEM_ZEROINIT, ServiceNameLen * sizeof(WCHAR)); + + if (*RelativeName == NULL) + { + DPRINT1("Error allocating memory for boot driver name!\n"); + return ERROR_NOT_ENOUGH_MEMORY; + } + + /* Copy it */ + wcscpy(*RelativeName, L"\SystemRoot\"); + wcscat(*RelativeName, CanonName + 13); + + DPRINT1("Bootdriver name %S\n", *RelativeName); + return ERROR_SUCCESS; + } + + /* Get buffer size needed for expanding env strings */ + BufferSize = ExpandEnvironmentStringsW(L"%SystemRoot%\", &Dest, 1); + + if (BufferSize <= 1) + { + DPRINT1("Error during a call to ExpandEnvironmentStringsW()\n"); + return ERROR_INVALID_ENVIRONMENT; + } + + /* Allocate memory, since the size is known now */ + Expanded = LocalAlloc(LMEM_ZEROINIT, BufferSize * sizeof(WCHAR) + sizeof(WCHAR)); + if (!Expanded) + { + DPRINT1("Error allocating memory for boot driver name!\n"); + return ERROR_NOT_ENOUGH_MEMORY; + } + + /* Expand it */ + if (ExpandEnvironmentStringsW(L"%SystemRoot%\", Expanded, BufferSize) > + BufferSize) + { + DPRINT1("Error during a call to ExpandEnvironmentStringsW()\n"); + LocalFree(Expanded); + return ERROR_NOT_ENOUGH_MEMORY; + } + + /* Convert to NY-style path */ + if (!RtlDosPathNameToNtPathName_U(Expanded, &NtPathName, NULL, NULL)) + { + DPRINT1("Error during a call to RtlDosPathNameToNtPathName_U()\n"); + return ERROR_INVALID_ENVIRONMENT; + } + + DPRINT("Converted to NT-style %wZ\n", &NtPathName); + + /* No need to keep the dos-path anymore */ + LocalFree(Expanded); + + /* Copy it to the allocated place */ + Expanded = LocalAlloc(LMEM_ZEROINIT, NtPathName.Length + sizeof(WCHAR)); + if (!Expanded) + { + DPRINT1("Error allocating memory for boot driver name!\n"); + return ERROR_NOT_ENOUGH_MEMORY; + } + + ExpandedLen = NtPathName.Length / sizeof(WCHAR); + wcsncpy(Expanded, NtPathName.Buffer, ExpandedLen); + Expanded[ExpandedLen] = 0; + + if (ServiceNameLen > ExpandedLen && + !wcsnicmp(Expanded, CanonName, ExpandedLen)) + { + /* Only \SystemRoot\ is missing */ + *RelativeName = LocalAlloc(LMEM_ZEROINIT, + (ServiceNameLen - ExpandedLen) * sizeof(WCHAR) + 13*sizeof(WCHAR)); + + if (*RelativeName == NULL) + { + DPRINT1("Error allocating memory for boot driver name!\n"); + LocalFree(Expanded); + return ERROR_NOT_ENOUGH_MEMORY; + } + + wcscpy(*RelativeName, L"\SystemRoot\"); + wcscat(*RelativeName, CanonName + ExpandedLen); + + RtlFreeUnicodeString(&NtPathName); + return ERROR_SUCCESS; + } + + /* The most complex case starts here */ + RtlInitUnicodeString(&SystemRoot, L"\SystemRoot"); + InitializeObjectAttributes(&ObjectAttributes, + &SystemRoot, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + /* Open this symlink */ + Status = NtOpenSymbolicLinkObject(&SymbolicLinkHandle, SYMBOLIC_LINK_QUERY, &ObjectAttributes); + + if (NT_SUCCESS(Status)) + { + LinkTarget.Length = 0; + LinkTarget.MaximumLength = 0; + + DPRINT("Opened symbolic link object\n"); + + Status = NtQuerySymbolicLinkObject(SymbolicLinkHandle, &LinkTarget, &BufferSize); + if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_TOO_SMALL) + { + /* Check if required buffer size is sane */ + if (BufferSize > 0xFFFD) + { + DPRINT1("Too large buffer required\n"); + *RelativeName = 0; + + if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle); + LocalFree(Expanded); + return ERROR_NOT_ENOUGH_MEMORY; + } + + /* Alloc the string */ + LinkTarget.Buffer = LocalAlloc(LMEM_ZEROINIT, BufferSize + sizeof(WCHAR)); + if (!LinkTarget.Buffer) + { + DPRINT1("Unable to alloc buffer\n"); + if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle); + LocalFree(Expanded); + return ERROR_NOT_ENOUGH_MEMORY; + } + + /* Do a real query now */ + LinkTarget.Length = BufferSize; + LinkTarget.MaximumLength = LinkTarget.Length + sizeof(WCHAR); + + Status = NtQuerySymbolicLinkObject(SymbolicLinkHandle, &LinkTarget, &BufferSize); + if (NT_SUCCESS(Status)) + { + DPRINT("LinkTarget: %wZ\n", &LinkTarget); + + ExpandedLen = LinkTarget.Length / sizeof(WCHAR); + if ((ServiceNameLen > ExpandedLen) && + !wcsnicmp(LinkTarget.Buffer, CanonName, ExpandedLen)) + { + *RelativeName = LocalAlloc(LMEM_ZEROINIT, + (ServiceNameLen - ExpandedLen) * sizeof(WCHAR) + 13*sizeof(WCHAR)); + + if (*RelativeName == NULL) + { + DPRINT1("Unable to alloc buffer\n"); + if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle); + LocalFree(Expanded); + RtlFreeUnicodeString(&NtPathName); + return ERROR_NOT_ENOUGH_MEMORY; + } + + /* Copy it over, substituting the first part + with SystemRoot */ + wcscpy(*RelativeName, L"\SystemRoot\"); + wcscat(*RelativeName, CanonName+ExpandedLen+1); + + /* Cleanup */ + if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle); + LocalFree(Expanded); + RtlFreeUnicodeString(&NtPathName); + + /* Return success */ + return ERROR_SUCCESS; + } + else + { + if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle); + LocalFree(Expanded); + RtlFreeUnicodeString(&NtPathName); + return ERROR_INVALID_PARAMETER; + } + } + else + { + DPRINT1("Error, Status = %08X\n", Status); + if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle); + LocalFree(Expanded); + RtlFreeUnicodeString(&NtPathName); + return ERROR_INVALID_PARAMETER; + } + } + else + { + DPRINT1("Error, Status = %08X\n", Status); + if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle); + LocalFree(Expanded); + RtlFreeUnicodeString(&NtPathName); + return ERROR_INVALID_PARAMETER; + } + } + else + { + DPRINT1("Error, Status = %08X\n", Status); + LocalFree(Expanded); + return ERROR_INVALID_PARAMETER; + } + + /* Failure */ + *RelativeName = NULL; + return ERROR_INVALID_PARAMETER; +} + +DWORD +ScmCanonDriverImagePath(DWORD dwStartType, + wchar_t *lpServiceName, + wchar_t **lpCanonName) +{ + DWORD ServiceNameLen, Result; + UNICODE_STRING NtServiceName; + WCHAR *RelativeName; + WCHAR *SourceName = lpServiceName; + + /* Calculate the length of the service's name */ + ServiceNameLen = wcslen(lpServiceName); + + /* 12 is wcslen(L"\SystemRoot\") */ + if (ServiceNameLen > 12 && + !wcsnicmp(L"\SystemRoot\", lpServiceName, 12)) + { + /* SystemRoot prefix is already included */ + + *lpCanonName = LocalAlloc(LMEM_ZEROINIT, ServiceNameLen * sizeof(WCHAR) + sizeof(WCHAR)); + + if (*lpCanonName == NULL) + { + DPRINT1("Error allocating memory for canonized service name!\n"); + return ERROR_NOT_ENOUGH_MEMORY; + } + + /* If it's a boot-time driver, it must be systemroot relative */ + if (dwStartType == SERVICE_BOOT_START) + SourceName += 12; + + /* Copy it */ + wcscpy(*lpCanonName, SourceName); + + DPRINT("Canonicalized name %S\n", *lpCanonName); + return NO_ERROR; + } + + /* Check if it has %SystemRoot% (len=13) */ + if (ServiceNameLen > 13 && + !wcsnicmp(L"%SystemRoot%\", lpServiceName, 13)) + { + /* Substitute %SystemRoot% with \SystemRoot\ */ + *lpCanonName = LocalAlloc(LMEM_ZEROINIT, ServiceNameLen * sizeof(WCHAR) + sizeof(WCHAR)); + + if (*lpCanonName == NULL) + { + DPRINT1("Error allocating memory for canonized service name!\n"); + return ERROR_NOT_ENOUGH_MEMORY; + } + + /* If it's a boot-time driver, it must be systemroot relative */ + if (dwStartType == SERVICE_BOOT_START) + wcscpy(*lpCanonName, L"\SystemRoot\"); + + wcscat(*lpCanonName, lpServiceName); + + DPRINT("Canonicalized name %S\n", *lpCanonName); + return NO_ERROR; + } + + /* Check if it's a relative path name */ + if (lpServiceName[0] != L'\' && lpServiceName[1] != L':') + { + *lpCanonName = LocalAlloc(LMEM_ZEROINIT, ServiceNameLen * sizeof(WCHAR) + sizeof(WCHAR)); + + if (*lpCanonName == NULL) + { + DPRINT1("Error allocating memory for canonized service name!\n"); + return ERROR_NOT_ENOUGH_MEMORY; + } + + /* Just copy it over without changing */ + wcscpy(*lpCanonName, lpServiceName); + + return NO_ERROR; + } + + /* It seems to be a DOS path, convert it */ + if (!RtlDosPathNameToNtPathName_U(lpServiceName, &NtServiceName, NULL, NULL)) + { + DPRINT1("RtlDosPathNameToNtPathName_U() failed!\n"); + return ERROR_INVALID_PARAMETER; + } + + *lpCanonName = LocalAlloc(LMEM_ZEROINIT, NtServiceName.Length + sizeof(WCHAR)); + + if (*lpCanonName == NULL) + { + DPRINT1("Error allocating memory for canonized service name!\n"); + RtlFreeUnicodeString(&NtServiceName); + return ERROR_NOT_ENOUGH_MEMORY; + } + + /* Copy the string */ + wcsncpy(*lpCanonName, NtServiceName.Buffer, NtServiceName.Length / sizeof(WCHAR)); + + /* The unicode string is not needed anymore */ + RtlFreeUnicodeString(&NtServiceName); + + if (dwStartType != SERVICE_BOOT_START) + { + DPRINT("Canonicalized name %S\n", *lpCanonName); + return NO_ERROR; + } + + /* The service is boot-started, so must be relative */ + Result = ScmConvertToBootPathName(*lpCanonName, &RelativeName); + if (Result) + { + /* There is a problem, free name and return */ + LocalFree(*lpCanonName); + DPRINT1("Error converting named!\n"); + return Result; + } + + ASSERT(RelativeName); + + /* Copy that string */ + wcscpy(*lpCanonName, RelativeName + 12); + + /* Free the allocated buffer */ + LocalFree(RelativeName); + + DPRINT("Canonicalized name %S\n", *lpCanonName); + + /* Success */ + return NO_ERROR; }
@@ -1058,24 +1432,12 @@
if (dwServiceType & SERVICE_DRIVER) { - lpImagePath = (WCHAR*) HeapAlloc(GetProcessHeap(), - HEAP_ZERO_MEMORY, - (wcslen(lpBinaryPathName) + 5) * sizeof(WCHAR)); - if (lpImagePath == NULL) - { - dwError = ERROR_NOT_ENOUGH_MEMORY; + dwError = ScmCanonDriverImagePath(dwStartType, + lpBinaryPathName, + &lpImagePath); + + if (dwError != ERROR_SUCCESS) goto done; - } - - if (lpBinaryPathName[1] == L':') - { - wcscpy(lpImagePath, L"\??\"); - wcscat(lpImagePath, lpBinaryPathName); - } - else - { - wcscpy(lpImagePath, lpBinaryPathName); - } }
/* Allocate a new service entry */