- Implement canonicalizing the service binary path properly, handling all possible cases (instead of a memory-overwriting hack Christoph noticed yesterday).

svn path=/trunk/; revision=31768
This commit is contained in:
Aleksey Bragin 2008-01-14 11:46:14 +00:00
parent 9f1182353d
commit c325864075

View file

@ -995,6 +995,380 @@ done:
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;
}
/* Function 12 */
DWORD
@ -1058,24 +1432,12 @@ ScmrCreateServiceW(handle_t BindingHandle,
if (dwServiceType & SERVICE_DRIVER)
{
lpImagePath = (WCHAR*) HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
(wcslen(lpBinaryPathName) + 5) * sizeof(WCHAR));
if (lpImagePath == NULL)
{
dwError = ERROR_NOT_ENOUGH_MEMORY;
goto done;
}
dwError = ScmCanonDriverImagePath(dwStartType,
lpBinaryPathName,
&lpImagePath);
if (lpBinaryPathName[1] == L':')
{
wcscpy(lpImagePath, L"\\??\\");
wcscat(lpImagePath, lpBinaryPathName);
}
else
{
wcscpy(lpImagePath, lpBinaryPathName);
}
if (dwError != ERROR_SUCCESS)
goto done;
}
/* Allocate a new service entry */