1131 lines
38 KiB
C
1131 lines
38 KiB
C
/*++
|
|
|
|
Copyright (c) 1997-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
safeget.cpp (SAFER SaferGetLevelInformation)
|
|
|
|
Abstract:
|
|
|
|
This module implements the WinSAFER APIs to get information about a
|
|
sepcific Authorization Level and the attributes and identities that
|
|
are associated with it.
|
|
|
|
Author:
|
|
|
|
Jeffrey Lawson (JLawson) - Nov 1999
|
|
|
|
Environment:
|
|
|
|
User mode only.
|
|
|
|
Exported Functions:
|
|
|
|
SaferGetLevelInformation
|
|
|
|
Revision History:
|
|
|
|
Created - Nov 1999
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
#pragma hdrstop
|
|
#include <winsafer.h>
|
|
#include <winsaferp.h>
|
|
#include "saferp.h"
|
|
|
|
|
|
|
|
DWORD NTAPI
|
|
__CodeAuthzpCountIdentsForLevel(
|
|
IN DWORD dwScopeId,
|
|
IN DWORD dwLevelId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines the number of Code Identities that have been associated
|
|
with a given WinSafer LevelId. This number represents the number
|
|
of identity GUIDs that will be returned to the caller.
|
|
|
|
Arguments:
|
|
|
|
dwScopeId - specifies the Scope value that will be considered.
|
|
|
|
dwLevelId - specifies the LevelId value to be counted.
|
|
|
|
Return Value:
|
|
|
|
Returns the actual number of unique Code Identities that were found
|
|
to be associated with the given LevelId. If no Identities were
|
|
found, then 0 will be returned.
|
|
|
|
--*/
|
|
{
|
|
PVOID RestartKey;
|
|
PAUTHZIDENTSTABLERECORD pAuthzIdentsRec;
|
|
DWORD dwMatchingCount = 0;
|
|
|
|
|
|
ASSERT(g_TableCritSec.OwningThread == UlongToHandle(GetCurrentThreadId()));
|
|
|
|
|
|
RestartKey = NULL;
|
|
for (pAuthzIdentsRec = (PAUTHZIDENTSTABLERECORD)
|
|
RtlEnumerateGenericTableWithoutSplaying(
|
|
&g_CodeIdentitiesTable, &RestartKey);
|
|
pAuthzIdentsRec != NULL;
|
|
pAuthzIdentsRec = (PAUTHZIDENTSTABLERECORD)
|
|
RtlEnumerateGenericTableWithoutSplaying(
|
|
&g_CodeIdentitiesTable, &RestartKey)
|
|
)
|
|
{
|
|
if (pAuthzIdentsRec->dwLevelId == dwLevelId &&
|
|
pAuthzIdentsRec->dwScopeId == dwScopeId)
|
|
dwMatchingCount++;
|
|
}
|
|
|
|
return dwMatchingCount;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS NTAPI
|
|
__CodeAuthzpFetchIdentsForLevel(
|
|
IN DWORD dwScopeId,
|
|
IN DWORD dwLevelId,
|
|
IN DWORD dwInBufferSize,
|
|
IN OUT LPVOID lpQueryBuffer,
|
|
OUT LPDWORD pdwUsedSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieves all of Code Identities that have been associated
|
|
with a given WinSafer LevelId. The number of identity GUIDs
|
|
that will be returned to the caller can be determined by
|
|
first calling __CodeAuthzpCountIdentsForLevel. It is assumed
|
|
that the caller has already determined and verified the appropriate
|
|
size of the buffer that should be supplied using that function.
|
|
|
|
Arguments:
|
|
|
|
dwScopeId - specifies the Scope value that will be considered.
|
|
|
|
dwLevelId - specifies the LevelId value to be matched.
|
|
|
|
dwInBufferSize - specifies the size of the output buffer.
|
|
|
|
lpQueryBuffer - points to the output buffer that should be filled.
|
|
|
|
pdwUsedSize - receives the actual number of bytes used.
|
|
|
|
Return Value:
|
|
|
|
Returns the actual number of unique Code Identities that were found
|
|
to be associated with the given LevelId. If no Identities were
|
|
found, then 0 will be returned.
|
|
|
|
--*/
|
|
{
|
|
PVOID RestartKey;
|
|
PAUTHZIDENTSTABLERECORD pIdentRecord;
|
|
LPVOID lpNextPtr;
|
|
|
|
|
|
ASSERT(g_TableCritSec.OwningThread == UlongToHandle(GetCurrentThreadId()));
|
|
ASSERT(ARGUMENT_PRESENT(lpQueryBuffer));
|
|
ASSERT(ARGUMENT_PRESENT(pdwUsedSize));
|
|
|
|
|
|
RestartKey = NULL;
|
|
lpNextPtr = (LPVOID) lpQueryBuffer;
|
|
for (pIdentRecord = (PAUTHZIDENTSTABLERECORD)
|
|
RtlEnumerateGenericTableWithoutSplaying(
|
|
&g_CodeIdentitiesTable, &RestartKey);
|
|
pIdentRecord != NULL;
|
|
pIdentRecord = (PAUTHZIDENTSTABLERECORD)
|
|
RtlEnumerateGenericTableWithoutSplaying(
|
|
&g_CodeIdentitiesTable, &RestartKey))
|
|
{
|
|
if (pIdentRecord->dwLevelId == dwLevelId &&
|
|
pIdentRecord->dwScopeId == dwScopeId)
|
|
{
|
|
if ( ((PBYTE) lpNextPtr) - ((PBYTE) lpQueryBuffer) +
|
|
sizeof(GUID) > dwInBufferSize ) {
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
RtlCopyMemory( lpNextPtr, &pIdentRecord->IdentGuid, sizeof(GUID) );
|
|
lpNextPtr = (LPVOID) ( ((PBYTE) lpNextPtr) + sizeof(GUID));
|
|
}
|
|
}
|
|
ASSERT((PBYTE) lpNextPtr <= ((PBYTE) lpQueryBuffer) + dwInBufferSize);
|
|
*pdwUsedSize = (DWORD) (((PBYTE) lpNextPtr) - ((PBYTE) lpQueryBuffer));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS NTAPI
|
|
__CodeAuthzpOpenIdentifierKey(
|
|
IN DWORD dwScopeId,
|
|
IN DWORD dwLevelId,
|
|
IN LPCWSTR szIdentityType,
|
|
IN REFGUID refIdentGuid,
|
|
OUT HANDLE *phOpenedKey
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
dwScopeId -
|
|
|
|
dwLevelId -
|
|
|
|
szIdentityType -
|
|
|
|
refIdentGuid -
|
|
|
|
phOpenedKey -
|
|
|
|
Return Value:
|
|
|
|
Returns STATUS_SUCCESS on success.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING UnicodePath;
|
|
WCHAR szPathBuffer[MAX_PATH];
|
|
|
|
|
|
if (!ARGUMENT_PRESENT(refIdentGuid) ||
|
|
!ARGUMENT_PRESENT(phOpenedKey)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto ExitHandler;
|
|
}
|
|
if (g_hKeyCustomRoot != NULL) {
|
|
if (dwScopeId != SAFER_SCOPEID_REGISTRY) {
|
|
Status = STATUS_INVALID_PARAMETER_MIX;
|
|
goto ExitHandler;
|
|
}
|
|
} else {
|
|
if (dwScopeId != SAFER_SCOPEID_MACHINE &&
|
|
dwScopeId != SAFER_SCOPEID_USER) {
|
|
Status = STATUS_INVALID_PARAMETER_MIX;
|
|
goto ExitHandler;
|
|
}
|
|
}
|
|
RtlInitEmptyUnicodeString(&UnicodePath,
|
|
szPathBuffer, sizeof(szPathBuffer));
|
|
Status = CodeAuthzpFormatIdentityKeyPath(
|
|
dwLevelId, szIdentityType,
|
|
refIdentGuid, &UnicodePath);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto ExitHandler;
|
|
}
|
|
Status = CodeAuthzpOpenPolicyRootKey(
|
|
dwScopeId, g_hKeyCustomRoot,
|
|
UnicodePath.Buffer,
|
|
KEY_READ, FALSE, phOpenedKey);
|
|
|
|
ExitHandler:
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS NTAPI
|
|
__CodeAuthzpQueryIdentityRegValue(
|
|
IN HANDLE hKeyIdentityBase,
|
|
IN LPWSTR szValueName,
|
|
IN DWORD dwRegType,
|
|
OUT PVOID lpOutBuffer,
|
|
IN ULONG ulOutBufferSize,
|
|
OUT PULONG pulActualOutSize OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generic helper function to query a registry value, provided that a
|
|
pre-opened registry handle to the key is already known.
|
|
|
|
Arguments:
|
|
|
|
hKeyIdentityBase - registry key handle.
|
|
|
|
szValueName - null-terminated Unicode string of the registry value name.
|
|
|
|
dwRegType - type of the registry value expected (REG_SZ, REG_DWORD, etc)
|
|
If a registry value of the given name exists, but is not of this
|
|
type, then this function will return STATUS_NOT_FOUND.
|
|
|
|
lpOutBuffer - pointer to a target buffer that will receive the
|
|
retrieved value contents.
|
|
|
|
ulOutBufferSize - input argument that specifies the maximum size
|
|
of the buffer pointed to by the lpOutBuffer argument.
|
|
|
|
pulActualOutSize - output argument that receives the actual size
|
|
of the retrieved value contents if the call is successful.
|
|
|
|
Return Value:
|
|
|
|
Returns STATUS_SUCCESS on success.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG ulResultLength;
|
|
UNICODE_STRING ValueName;
|
|
ULONG ulValueBufferSize;
|
|
PKEY_VALUE_PARTIAL_INFORMATION pValuePartialInfo;
|
|
|
|
|
|
|
|
//
|
|
// Allocate enough memory for the query buffer.
|
|
//
|
|
ASSERT(ARGUMENT_PRESENT(lpOutBuffer) && ulOutBufferSize > 0);
|
|
ulValueBufferSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) +
|
|
ulOutBufferSize + sizeof(WCHAR) * 256;
|
|
pValuePartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION)
|
|
RtlAllocateHeap(RtlProcessHeap(), 0, ulValueBufferSize);
|
|
if (!pValuePartialInfo) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto ExitHandler;
|
|
}
|
|
|
|
|
|
//
|
|
// Actually query the value into the temporary query buffer.
|
|
//
|
|
ASSERT(ARGUMENT_PRESENT(szValueName));
|
|
RtlInitUnicodeString(&ValueName, szValueName);
|
|
Status = NtQueryValueKey(hKeyIdentityBase, &ValueName,
|
|
KeyValuePartialInformation,
|
|
pValuePartialInfo, ulValueBufferSize,
|
|
&ulResultLength);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto ExitHandler2;
|
|
}
|
|
if (pValuePartialInfo->Type != dwRegType) {
|
|
Status = STATUS_NOT_FOUND;
|
|
goto ExitHandler2;
|
|
}
|
|
|
|
|
|
//
|
|
// Copy the resulting data from the query buffer into
|
|
// the caller's buffer.
|
|
//
|
|
|
|
ulResultLength = pValuePartialInfo->DataLength;
|
|
if (ulResultLength > ulOutBufferSize) {
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
} else {
|
|
RtlCopyMemory(lpOutBuffer,
|
|
pValuePartialInfo->Data,
|
|
ulResultLength);
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(pulActualOutSize)) {
|
|
*pulActualOutSize = ulResultLength;
|
|
}
|
|
|
|
ExitHandler2:
|
|
RtlFreeHeap(RtlProcessHeap(), 0, (PVOID) pValuePartialInfo);
|
|
|
|
|
|
ExitHandler:
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS NTAPI
|
|
__CodeAuthzpQuerySingleIdentification(
|
|
IN PAUTHZIDENTSTABLERECORD pSingleIdentRecord,
|
|
OUT LPVOID lpQueryBuffer,
|
|
IN DWORD dwInBufferSize,
|
|
OUT PDWORD dwNeededSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allows the user to retrieve information about a single identity.
|
|
|
|
Assumes that the caller has already obtained and locked the
|
|
global critical section.
|
|
|
|
Arguments:
|
|
|
|
pSingleIdentRecord - pointer to the identity record structure.
|
|
|
|
lpQueryBuffer - pointer to a user-supplied memory buffer that
|
|
will receive the requested information.
|
|
|
|
dwInBufferSize - specifies the size of the user's memory block.
|
|
|
|
lpdwOutBufferSize - receives the used size of the data within the
|
|
memory block, or the minimum necessary size if the passed
|
|
buffer was too small.
|
|
|
|
Return Value:
|
|
|
|
Returns STATUS_SUCCESS on success.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
HANDLE hKeyIdentity = NULL;
|
|
ULONG ulResultLength = 0;
|
|
ULONG ulTextLen = 0;
|
|
PSAFER_IDENTIFICATION_HEADER pIdentCommon = NULL;
|
|
|
|
//
|
|
// All of these conditions should have been already verified
|
|
// by our caller before calling us, so we only assert them.
|
|
//
|
|
ASSERT(ARGUMENT_PRESENT(pSingleIdentRecord));
|
|
ASSERT(ARGUMENT_PRESENT(lpQueryBuffer) &&
|
|
dwInBufferSize >= sizeof(SAFER_IDENTIFICATION_HEADER));
|
|
ASSERT(pSingleIdentRecord->dwIdentityType == SaferIdentityTypeImageName ||
|
|
pSingleIdentRecord->dwIdentityType == SaferIdentityTypeImageHash ||
|
|
pSingleIdentRecord->dwIdentityType == SaferIdentityTypeUrlZone);
|
|
|
|
|
|
//
|
|
// Start filling the resulting structure with the data.
|
|
//
|
|
pIdentCommon = (PSAFER_IDENTIFICATION_HEADER) lpQueryBuffer;
|
|
switch (pSingleIdentRecord->dwIdentityType)
|
|
{
|
|
// --------------------
|
|
|
|
case SaferIdentityTypeImageName:
|
|
Status = __CodeAuthzpOpenIdentifierKey(
|
|
pSingleIdentRecord->dwScopeId,
|
|
pSingleIdentRecord->dwLevelId,
|
|
SAFER_PATHS_REGSUBKEY,
|
|
&pSingleIdentRecord->IdentGuid,
|
|
&hKeyIdentity);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto ExitHandler;
|
|
}
|
|
|
|
pIdentCommon->dwIdentificationType = SaferIdentityTypeImageName;
|
|
{
|
|
PSAFER_PATHNAME_IDENTIFICATION pIdentOut = (PSAFER_PATHNAME_IDENTIFICATION) lpQueryBuffer;
|
|
|
|
ASSERT(&pIdentOut->header == pIdentCommon);
|
|
|
|
|
|
ulTextLen = pSingleIdentRecord->ImageNameInfo.ImagePath.Length;
|
|
|
|
*dwNeededSize = ulTextLen + sizeof(UNICODE_NULL) +
|
|
sizeof(SAFER_PATHNAME_IDENTIFICATION);
|
|
|
|
if (*dwNeededSize > dwInBufferSize) {
|
|
// the imagepath is vital, so we'll bail out.
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
goto ExitHandler2;
|
|
}
|
|
|
|
pIdentOut->ImageName = (PWCHAR) (((PUCHAR) pIdentOut) + sizeof(SAFER_PATHNAME_IDENTIFICATION));
|
|
|
|
Status = __CodeAuthzpQueryIdentityRegValue(
|
|
hKeyIdentity,
|
|
SAFER_IDS_DESCRIPTION_REGVALUE,
|
|
REG_SZ,
|
|
pIdentOut->Description,
|
|
SAFER_MAX_DESCRIPTION_SIZE * sizeof(WCHAR),
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
pIdentOut->Description[0] = UNICODE_NULL;
|
|
}
|
|
|
|
RtlCopyMemory(pIdentOut->ImageName,
|
|
pSingleIdentRecord->ImageNameInfo.ImagePath.Buffer,
|
|
ulTextLen);
|
|
pIdentOut->ImageName[ulTextLen / sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
pIdentOut->header.cbStructSize = *dwNeededSize;
|
|
|
|
pIdentOut->dwSaferFlags =
|
|
pSingleIdentRecord->ImageNameInfo.dwSaferFlags;
|
|
}
|
|
break;
|
|
|
|
// --------------------
|
|
|
|
case SaferIdentityTypeImageHash:
|
|
Status = __CodeAuthzpOpenIdentifierKey(
|
|
pSingleIdentRecord->dwScopeId,
|
|
pSingleIdentRecord->dwLevelId,
|
|
SAFER_HASHMD5_REGSUBKEY,
|
|
&pSingleIdentRecord->IdentGuid,
|
|
&hKeyIdentity);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto ExitHandler;
|
|
}
|
|
|
|
pIdentCommon->dwIdentificationType = SaferIdentityTypeImageHash;
|
|
{
|
|
PSAFER_HASH_IDENTIFICATION pIdentOut = (PSAFER_HASH_IDENTIFICATION) lpQueryBuffer;
|
|
|
|
ASSERT(&pIdentOut->header == pIdentCommon);
|
|
pIdentOut->header.cbStructSize =
|
|
sizeof(SAFER_HASH_IDENTIFICATION);
|
|
|
|
Status = __CodeAuthzpQueryIdentityRegValue(
|
|
hKeyIdentity,
|
|
SAFER_IDS_DESCRIPTION_REGVALUE,
|
|
REG_SZ,
|
|
pIdentOut->Description,
|
|
SAFER_MAX_DESCRIPTION_SIZE * sizeof(WCHAR),
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
pIdentOut->Description[0] = UNICODE_NULL;
|
|
}
|
|
Status = __CodeAuthzpQueryIdentityRegValue(
|
|
hKeyIdentity,
|
|
SAFER_IDS_FRIENDLYNAME_REGVALUE,
|
|
REG_SZ,
|
|
pIdentOut->FriendlyName,
|
|
SAFER_MAX_FRIENDLYNAME_SIZE * sizeof(WCHAR),
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
pIdentOut->FriendlyName[0] = UNICODE_NULL;
|
|
}
|
|
ASSERT(pSingleIdentRecord->ImageHashInfo.HashSize <= SAFER_MAX_HASH_SIZE);
|
|
RtlCopyMemory(pIdentOut->ImageHash,
|
|
pSingleIdentRecord->ImageHashInfo.ImageHash,
|
|
pSingleIdentRecord->ImageHashInfo.HashSize);
|
|
pIdentOut->HashSize =
|
|
pSingleIdentRecord->ImageHashInfo.HashSize;
|
|
pIdentOut->HashAlgorithm =
|
|
pSingleIdentRecord->ImageHashInfo.HashAlgorithm;
|
|
pIdentOut->ImageSize =
|
|
pSingleIdentRecord->ImageHashInfo.ImageSize;
|
|
pIdentOut->dwSaferFlags =
|
|
pSingleIdentRecord->ImageHashInfo.dwSaferFlags;
|
|
}
|
|
break;
|
|
|
|
// --------------------
|
|
|
|
case SaferIdentityTypeUrlZone:
|
|
{
|
|
PSAFER_URLZONE_IDENTIFICATION pIdentOut = (PSAFER_URLZONE_IDENTIFICATION) lpQueryBuffer;
|
|
|
|
Status = __CodeAuthzpOpenIdentifierKey(
|
|
pSingleIdentRecord->dwScopeId,
|
|
pSingleIdentRecord->dwLevelId,
|
|
SAFER_SOURCEURL_REGSUBKEY,
|
|
&pSingleIdentRecord->IdentGuid,
|
|
&hKeyIdentity);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto ExitHandler;
|
|
}
|
|
pIdentCommon->dwIdentificationType = SaferIdentityTypeUrlZone;
|
|
|
|
ASSERT(&pIdentOut->header == pIdentCommon);
|
|
pIdentOut->header.cbStructSize =
|
|
sizeof(SAFER_URLZONE_IDENTIFICATION);
|
|
|
|
pIdentOut->UrlZoneId =
|
|
pSingleIdentRecord->ImageZone.UrlZoneId;
|
|
pIdentOut->dwSaferFlags =
|
|
pSingleIdentRecord->ImageZone.dwSaferFlags;
|
|
break;
|
|
}
|
|
|
|
// --------------------
|
|
|
|
default:
|
|
Status = STATUS_INVALID_INFO_CLASS;
|
|
goto ExitHandler;
|
|
}
|
|
|
|
//
|
|
// Fill in the other information that is applicable to all types.
|
|
//
|
|
RtlCopyMemory(&pIdentCommon->IdentificationGuid,
|
|
&pSingleIdentRecord->IdentGuid,
|
|
sizeof(GUID));
|
|
ASSERT(sizeof(FILETIME) == sizeof(DWORD) * 2);
|
|
Status = __CodeAuthzpQueryIdentityRegValue(
|
|
hKeyIdentity,
|
|
SAFER_IDS_LASTMODIFIED_REGVALUE,
|
|
REG_QWORD,
|
|
&pIdentCommon->lastModified,
|
|
sizeof(FILETIME),
|
|
&ulResultLength
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
pIdentCommon->lastModified.dwHighDateTime =
|
|
pIdentCommon->lastModified.dwLowDateTime = 0;
|
|
}
|
|
Status = STATUS_SUCCESS;
|
|
|
|
ExitHandler2:
|
|
NtClose(hKeyIdentity);
|
|
|
|
ExitHandler:
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS NTAPI
|
|
__CodeAuthzpGetAuthzLevelInfo(
|
|
IN SAFER_LEVEL_HANDLE hLevelHandle,
|
|
IN SAFER_OBJECT_INFO_CLASS dwInfoType,
|
|
OUT LPVOID lpQueryBuffer OPTIONAL,
|
|
IN DWORD dwInBufferSize,
|
|
OUT LPDWORD lpdwOutBufferSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allows the user to query various pieces of information about a
|
|
given Level handle.
|
|
|
|
Assumes that the caller has already obtained and locked the
|
|
global critical section.
|
|
|
|
Arguments:
|
|
|
|
hLevelHandle - the handle to the authorization object to evaluate.
|
|
|
|
dwInfoType - specifies the type of information being requested.
|
|
|
|
lpQueryBuffer - pointer to a user-supplied memory buffer that
|
|
will receive the requested information.
|
|
|
|
dwInBufferSize - specifies the size of the user's memory block.
|
|
|
|
lpdwOutBufferSize - receives the used size of the data within the
|
|
memory block, or the minimum necessary size if the passed
|
|
buffer was too small.
|
|
|
|
Return Value:
|
|
|
|
Returns STATUS_SUCCESS on success.
|
|
|
|
--*/
|
|
{
|
|
const static SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
|
|
NTSTATUS Status;
|
|
PAUTHZLEVELHANDLESTRUCT pAuthzLevelStruct;
|
|
PAUTHZLEVELTABLERECORD pAuthzLevelRecord;
|
|
PAUTHZIDENTSTABLERECORD pSingleIdentRecord = NULL;
|
|
DWORD dwHandleScopeId;
|
|
|
|
|
|
|
|
|
|
//
|
|
// Obtain a pointer to the authorization Level structure.
|
|
//
|
|
ASSERT(g_TableCritSec.OwningThread == UlongToHandle(GetCurrentThreadId()));
|
|
Status = CodeAuthzHandleToLevelStruct(hLevelHandle, &pAuthzLevelStruct);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto ExitHandler;
|
|
}
|
|
ASSERT(pAuthzLevelStruct != NULL);
|
|
pAuthzLevelRecord = CodeAuthzLevelObjpLookupByLevelId(
|
|
&g_CodeLevelObjTable, pAuthzLevelStruct->dwLevelId);
|
|
if (!pAuthzLevelRecord) {
|
|
Status = STATUS_INVALID_HANDLE;
|
|
goto ExitHandler;
|
|
}
|
|
dwHandleScopeId = pAuthzLevelStruct->dwScopeId;
|
|
|
|
|
|
//
|
|
// Some of the attributes are fixed size, or are known before performing
|
|
// the full query against the registry. Compute their size first.
|
|
//
|
|
*lpdwOutBufferSize = 0;
|
|
switch (dwInfoType)
|
|
{
|
|
case SaferObjectLevelId: // DWORD
|
|
case SaferObjectScopeId: // DWORD
|
|
case SaferObjectBuiltin: // DWORD boolean
|
|
*lpdwOutBufferSize = sizeof(DWORD);
|
|
break;
|
|
|
|
case SaferObjectFriendlyName: // LPCTSTR
|
|
if (!pAuthzLevelRecord->UnicodeFriendlyName.Buffer ||
|
|
!pAuthzLevelRecord->UnicodeFriendlyName.Length) {
|
|
Status = STATUS_NOT_FOUND;
|
|
goto ExitHandler;
|
|
}
|
|
*lpdwOutBufferSize =
|
|
pAuthzLevelRecord->UnicodeFriendlyName.Length +
|
|
sizeof(UNICODE_NULL);
|
|
break;
|
|
|
|
case SaferObjectDescription: // LPCTSTR
|
|
if (!pAuthzLevelRecord->UnicodeDescription.Buffer ||
|
|
!pAuthzLevelRecord->UnicodeDescription.Length) {
|
|
Status = STATUS_NOT_FOUND;
|
|
goto ExitHandler;
|
|
}
|
|
*lpdwOutBufferSize = pAuthzLevelRecord->UnicodeDescription.Length +
|
|
sizeof(UNICODE_NULL);
|
|
break;
|
|
|
|
#ifdef ALLOW_FULL_WINSAFER
|
|
case SaferObjectDisallowed: // DWORD boolean
|
|
case SaferObjectDisableMaxPrivilege: // DWORD boolean
|
|
case SaferObjectInvertDeletedPrivileges: // DWORD boolean
|
|
*lpdwOutBufferSize = sizeof(DWORD);
|
|
break;
|
|
|
|
case SaferObjectDeletedPrivileges: // TOKEN_PRIVILEGES
|
|
*lpdwOutBufferSize = (sizeof(TOKEN_PRIVILEGES) - sizeof(LUID_AND_ATTRIBUTES)) +
|
|
pAuthzLevelRecord->DeletePrivilegeUsedCount * sizeof(LUID_AND_ATTRIBUTES);
|
|
break;
|
|
|
|
case SaferObjectDefaultOwner: // TOKEN_OWNER
|
|
*lpdwOutBufferSize = sizeof(TOKEN_OWNER);
|
|
if (pAuthzLevelRecord->DefaultOwner != NULL)
|
|
*lpdwOutBufferSize += RtlLengthSid(pAuthzLevelRecord->DefaultOwner);
|
|
break;
|
|
|
|
case SaferObjectSidsToDisable: // TOKEN_GROUPS
|
|
*lpdwOutBufferSize = (sizeof(TOKEN_GROUPS) - sizeof(SID_AND_ATTRIBUTES)) +
|
|
pAuthzLevelRecord->DisableSidUsedCount * sizeof(SID_AND_ATTRIBUTES);
|
|
for (Index = 0; Index < pAuthzLevelRecord->DisableSidUsedCount; Index++)
|
|
*lpdwOutBufferSize += RtlLengthSid(pAuthzLevelRecord->SidsToDisable[Index].Sid);
|
|
break;
|
|
|
|
case SaferObjectRestrictedSidsInverted: // TOKEN_GROUPS
|
|
*lpdwOutBufferSize = (sizeof(TOKEN_GROUPS) - sizeof(SID_AND_ATTRIBUTES)) +
|
|
pAuthzLevelRecord->RestrictedSidsInvUsedCount * sizeof(SID_AND_ATTRIBUTES);
|
|
for (Index = 0; Index < pAuthzLevelRecord->RestrictedSidsInvUsedCount; Index++)
|
|
*lpdwOutBufferSize += RtlLengthSid(pAuthzLevelRecord->RestrictedSidsInv[Index].Sid);
|
|
break;
|
|
|
|
case SaferObjectRestrictedSidsAdded: // TOKEN_GROUPS
|
|
*lpdwOutBufferSize = (sizeof(TOKEN_GROUPS) - sizeof(SID_AND_ATTRIBUTES)) +
|
|
pAuthzLevelRecord->RestrictedSidsAddedUsedCount * sizeof(SID_AND_ATTRIBUTES);
|
|
for (Index = 0; Index < pAuthzLevelRecord->RestrictedSidsAddedUsedCount; Index++)
|
|
*lpdwOutBufferSize += RtlLengthSid(pAuthzLevelRecord->RestrictedSidsAdded[Index].Sid);
|
|
break;
|
|
#endif
|
|
|
|
|
|
case SaferObjectAllIdentificationGuids:
|
|
*lpdwOutBufferSize = sizeof(GUID) *
|
|
__CodeAuthzpCountIdentsForLevel(
|
|
dwHandleScopeId,
|
|
pAuthzLevelRecord->dwLevelId);
|
|
if (!*lpdwOutBufferSize) {
|
|
Status = STATUS_NOT_FOUND;
|
|
goto ExitHandler;
|
|
}
|
|
break;
|
|
|
|
|
|
case SaferObjectSingleIdentification:
|
|
{
|
|
*lpdwOutBufferSize = sizeof(SAFER_IDENTIFICATION_HEADER);
|
|
if (ARGUMENT_PRESENT(lpQueryBuffer) &&
|
|
dwInBufferSize >= *lpdwOutBufferSize)
|
|
{
|
|
PSAFER_IDENTIFICATION_HEADER pIdentCommonHeader =
|
|
(PSAFER_IDENTIFICATION_HEADER) lpQueryBuffer;
|
|
|
|
if (pIdentCommonHeader->cbStructSize < *lpdwOutBufferSize) {
|
|
// the caller claimed that the dwInBufferSize was
|
|
// large enough, but the common header size doesn't.
|
|
goto ExitBufferTooSmall;
|
|
}
|
|
|
|
|
|
if (IsZeroGUID(&pIdentCommonHeader->IdentificationGuid))
|
|
{
|
|
//
|
|
// Caller supplied a zero GUID and wants to retrieve
|
|
// the rule that produced the SaferIdentifyLevel
|
|
// result match, if this Level handle was from it.
|
|
//
|
|
if (IsZeroGUID(&pAuthzLevelStruct->identGuid)) {
|
|
// This was a handle that was explicitly opened
|
|
// by the user with SaferCreateLevel().
|
|
Status = STATUS_NOT_FOUND;
|
|
goto ExitHandler;
|
|
}
|
|
pSingleIdentRecord = CodeAuthzIdentsLookupByGuid(
|
|
&g_CodeIdentitiesTable,
|
|
&pAuthzLevelStruct->identGuid);
|
|
if (!pSingleIdentRecord) {
|
|
// This handle was obtained via a match to one of
|
|
// the special GUIDs or an code identity GUID
|
|
// that no longer exists. Just return a blank
|
|
// structure with just the GUID in the header.
|
|
*lpdwOutBufferSize = sizeof(SAFER_IDENTIFICATION_HEADER);
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Caller is explicitly supplying the GUID of the
|
|
// code identifier rule that details should be
|
|
// retrieved for.
|
|
//
|
|
pSingleIdentRecord = CodeAuthzIdentsLookupByGuid(
|
|
&g_CodeIdentitiesTable,
|
|
&pIdentCommonHeader->IdentificationGuid);
|
|
}
|
|
|
|
|
|
//
|
|
// We now have a pointer to the identity record that
|
|
// information should be retrieved for. Perform the
|
|
// necessary work to marshal back the details about it.
|
|
//
|
|
if (!pSingleIdentRecord ||
|
|
pSingleIdentRecord->dwLevelId !=
|
|
pAuthzLevelRecord->dwLevelId ||
|
|
pSingleIdentRecord->dwScopeId != dwHandleScopeId)
|
|
{
|
|
Status = STATUS_NOT_FOUND;
|
|
goto ExitHandler;
|
|
}
|
|
switch (pSingleIdentRecord->dwIdentityType) {
|
|
case SaferIdentityTypeImageName:
|
|
// Size is calculated later on.
|
|
*lpdwOutBufferSize = 0;
|
|
break;
|
|
|
|
case SaferIdentityTypeImageHash:
|
|
*lpdwOutBufferSize = sizeof(SAFER_HASH_IDENTIFICATION);
|
|
break;
|
|
|
|
case SaferIdentityTypeUrlZone:
|
|
*lpdwOutBufferSize = sizeof(SAFER_URLZONE_IDENTIFICATION);
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_NOT_FOUND;
|
|
goto ExitHandler;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SaferObjectExtendedError:
|
|
*lpdwOutBufferSize = sizeof(DWORD);
|
|
break;
|
|
|
|
|
|
default:
|
|
Status = STATUS_INVALID_INFO_CLASS;
|
|
goto ExitHandler;
|
|
}
|
|
//ASSERTMSG("required buffer size must be computed", *lpdwOutBufferSize != 0);
|
|
|
|
|
|
//
|
|
// If there is not enough space for the query, then return with error.
|
|
//
|
|
if (*lpdwOutBufferSize != -1 &&
|
|
(!ARGUMENT_PRESENT(lpQueryBuffer) ||
|
|
dwInBufferSize < *lpdwOutBufferSize) )
|
|
{
|
|
ExitBufferTooSmall:
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
goto ExitHandler;
|
|
}
|
|
|
|
|
|
//
|
|
// Otherwise there is enough space for the request buffer,
|
|
// so now actually perform the copy.
|
|
//
|
|
switch (dwInfoType)
|
|
{
|
|
case SaferObjectLevelId: // DWORD
|
|
*(PDWORD)lpQueryBuffer = pAuthzLevelRecord->dwLevelId;
|
|
break;
|
|
|
|
case SaferObjectScopeId: // DWORD
|
|
*(PDWORD)lpQueryBuffer = dwHandleScopeId;
|
|
break;
|
|
|
|
case SaferObjectBuiltin: // DWORD boolean
|
|
*((LPDWORD)lpQueryBuffer) =
|
|
(pAuthzLevelRecord->Builtin ? TRUE : FALSE);
|
|
break;
|
|
|
|
case SaferObjectExtendedError:
|
|
*((DWORD *)lpQueryBuffer) = pAuthzLevelStruct->dwExtendedError;
|
|
break;
|
|
|
|
case SaferObjectFriendlyName: // LPCTSTR
|
|
RtlCopyMemory(lpQueryBuffer,
|
|
pAuthzLevelRecord->UnicodeFriendlyName.Buffer,
|
|
pAuthzLevelRecord->UnicodeFriendlyName.Length);
|
|
((LPWSTR) lpQueryBuffer)[
|
|
pAuthzLevelRecord->UnicodeFriendlyName.Length /
|
|
sizeof(WCHAR) ] = UNICODE_NULL;
|
|
*lpdwOutBufferSize =
|
|
pAuthzLevelRecord->UnicodeFriendlyName.Length +
|
|
sizeof(UNICODE_NULL);
|
|
break;
|
|
|
|
|
|
case SaferObjectDescription: // LPCTSTR
|
|
|
|
RtlCopyMemory(lpQueryBuffer,
|
|
pAuthzLevelRecord->UnicodeDescription.Buffer,
|
|
pAuthzLevelRecord->UnicodeDescription.Length);
|
|
((LPWSTR) lpQueryBuffer)[
|
|
pAuthzLevelRecord->UnicodeDescription.Length /
|
|
sizeof(WCHAR)] = UNICODE_NULL;
|
|
*lpdwOutBufferSize =
|
|
pAuthzLevelRecord->UnicodeDescription.Length +
|
|
sizeof(UNICODE_NULL);
|
|
break;
|
|
|
|
#ifdef ALLOW_FULL_WINSAFER
|
|
case SaferObjectDisallowed: // DWORD boolean
|
|
*((LPDWORD)lpQueryBuffer) = (pAuthzLevelRecord->DisallowExecution != 0) ? TRUE : FALSE;
|
|
break;
|
|
case SaferObjectDisableMaxPrivilege: // DWORD boolean
|
|
*((LPDWORD)lpQueryBuffer) = (pAuthzLevelRecord->Flags & DISABLE_MAX_PRIVILEGE) != 0;
|
|
break;
|
|
case SaferObjectInvertDeletedPrivileges: // DWORD boolean
|
|
*((LPDWORD)lpQueryBuffer) = (pAuthzLevelRecord->InvertDeletePrivs != 0) ? TRUE : FALSE;
|
|
break;
|
|
case SaferObjectDeletedPrivileges: // TOKEN_PRIVILEGES
|
|
{
|
|
PTOKEN_PRIVILEGES pTokenPrivs = (PTOKEN_PRIVILEGES) lpQueryBuffer;
|
|
pTokenPrivs->PrivilegeCount = pAuthzLevelRecord->DeletePrivilegeUsedCount;
|
|
RtlCopyMemory(&pTokenPrivs->Privileges[0],
|
|
&pAuthzLevelRecord->PrivilegesToDelete[0],
|
|
sizeof(LUID_AND_ATTRIBUTES) * pAuthzLevelRecord->DeletePrivilegeUsedCount);
|
|
break;
|
|
}
|
|
case SaferObjectDefaultOwner: // TOKEN_OWNER
|
|
{
|
|
PTOKEN_OWNER pTokenOwner = (PTOKEN_OWNER) lpQueryBuffer;
|
|
if (pAuthzLevelRecord->DefaultOwner == NULL)
|
|
pTokenOwner->Owner = NULL;
|
|
else {
|
|
pTokenOwner->Owner = (PSID) &pTokenOwner[1];
|
|
Status = RtlCopySid(dwInBufferSize - sizeof(TOKEN_OWNER),
|
|
pTokenOwner->Owner, pAuthzLevelRecord->DefaultOwner);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
}
|
|
break;
|
|
}
|
|
case SaferObjectSidsToDisable: // TOKEN_GROUPS (wildcard sids)
|
|
{
|
|
PTOKEN_GROUPS pTokenGroups = (PTOKEN_GROUPS) lpQueryBuffer;
|
|
DWORD dwUsedOffset = (sizeof(TOKEN_GROUPS) - sizeof(SID_AND_ATTRIBUTES)) +
|
|
(sizeof(SID_AND_ATTRIBUTES) * pAuthzLevelRecord->DisableSidUsedCount);
|
|
pTokenGroups->GroupCount = pAuthzLevelRecord->DisableSidUsedCount;
|
|
for (Index = 0; Index < pAuthzLevelRecord->DisableSidUsedCount; Index++) {
|
|
pTokenGroups->Groups[Index].Sid = (PSID) &((LPBYTE)lpQueryBuffer)[dwUsedOffset];
|
|
DWORD dwSidLength = RtlLengthSid(pAuthzLevelRecord->SidsToDisable[Index].Sid);
|
|
ASSERT(dwUsedOffset + dwSidLength <= dwInBufferSize);
|
|
RtlCopyMemory(pTokenGroups->Groups[Index].Sid,
|
|
pAuthzLevelRecord->SidsToDisable[Index].Sid, dwSidLength);
|
|
dwUsedOffset += dwSidLength;
|
|
|
|
//BLACKCOMB TODO: handle wildcard sids differently?
|
|
if (pAuthzLevelRecord->SidsToDisable[Index].WildcardPos == -1)
|
|
pTokenGroups->Groups[Index].Attributes = 0;
|
|
else
|
|
pTokenGroups->Groups[Index].Attributes = (((DWORD) '*') << 24) |
|
|
(pAuthzLevelRecord->SidsToDisable[Index].WildcardPos & 0x0000FFFF);
|
|
}
|
|
break;
|
|
}
|
|
case SaferObjectRestrictedSidsInverted: // TOKEN_GROUPS (wildcard sids)
|
|
{
|
|
PTOKEN_GROUPS pTokenGroups = (PTOKEN_GROUPS) lpQueryBuffer;
|
|
DWORD dwUsedOffset = (sizeof(TOKEN_GROUPS) - sizeof(SID_AND_ATTRIBUTES)) +
|
|
(sizeof(SID_AND_ATTRIBUTES) * pAuthzLevelRecord->RestrictedSidsInvUsedCount);
|
|
pTokenGroups->GroupCount = pAuthzLevelRecord->RestrictedSidsInvUsedCount;
|
|
for (Index = 0; Index < pAuthzLevelRecord->RestrictedSidsInvUsedCount; Index++) {
|
|
pTokenGroups->Groups[Index].Sid = (PSID) &((LPBYTE)lpQueryBuffer)[dwUsedOffset];
|
|
DWORD dwSidLength = RtlLengthSid(pAuthzLevelRecord->RestrictedSidsInv[Index].Sid);
|
|
ASSERT(dwUsedOffset + dwSidLength <= dwInBufferSize);
|
|
RtlCopyMemory(pTokenGroups->Groups[Index].Sid,
|
|
pAuthzLevelRecord->RestrictedSidsInv[Index].Sid, dwSidLength);
|
|
dwUsedOffset += dwSidLength;
|
|
|
|
//BLACKCOMB TODO: handle wildcard sids differently?
|
|
if (pAuthzLevelRecord->RestrictedSidsInv[Index].WildcardPos == -1)
|
|
pTokenGroups->Groups[Index].Attributes = 0;
|
|
else
|
|
pTokenGroups->Groups[Index].Attributes = (((DWORD) '*') << 24) |
|
|
(pAuthzLevelRecord->RestrictedSidsInv[Index].WildcardPos & 0x0000FFFF);
|
|
}
|
|
break;
|
|
}
|
|
case SaferObjectRestrictedSidsAdded: // TOKEN_GROUPS
|
|
{
|
|
PTOKEN_GROUPS pTokenGroups = (PTOKEN_GROUPS) lpQueryBuffer;
|
|
DWORD dwUsedOffset = (sizeof(TOKEN_GROUPS) - sizeof(SID_AND_ATTRIBUTES)) +
|
|
(sizeof(SID_AND_ATTRIBUTES) * pAuthzLevelRecord->RestrictedSidsAddedUsedCount);
|
|
pTokenGroups->GroupCount = pAuthzLevelRecord->RestrictedSidsAddedUsedCount;
|
|
for (Index = 0; Index < pAuthzLevelRecord->RestrictedSidsAddedUsedCount; Index++) {
|
|
pTokenGroups->Groups[Index].Attributes = 0;
|
|
pTokenGroups->Groups[Index].Sid = (PSID) &((LPBYTE)lpQueryBuffer)[dwUsedOffset];
|
|
DWORD dwSidLength = RtlLengthSid(pAuthzLevelRecord->RestrictedSidsAdded[Index].Sid);
|
|
ASSERT(dwUsedOffset + dwSidLength <= dwInBufferSize);
|
|
RtlCopyMemory(pTokenGroups->Groups[Index].Sid,
|
|
pAuthzLevelRecord->RestrictedSidsAdded[Index].Sid, dwSidLength);
|
|
dwUsedOffset += dwSidLength;
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
case SaferObjectAllIdentificationGuids:
|
|
Status = __CodeAuthzpFetchIdentsForLevel(
|
|
dwHandleScopeId,
|
|
pAuthzLevelRecord->dwLevelId,
|
|
dwInBufferSize,
|
|
lpQueryBuffer,
|
|
lpdwOutBufferSize);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto ExitHandler;
|
|
}
|
|
break;
|
|
|
|
case SaferObjectSingleIdentification:
|
|
if (pSingleIdentRecord == NULL)
|
|
{
|
|
// One of the special identifier GUIDs is being returned,
|
|
// or a no-longer existing identifier GUID.
|
|
PSAFER_IDENTIFICATION_HEADER pCommon =
|
|
(PSAFER_IDENTIFICATION_HEADER) lpQueryBuffer;
|
|
|
|
ASSERT(*lpdwOutBufferSize == sizeof(SAFER_IDENTIFICATION_HEADER));
|
|
RtlZeroMemory(pCommon, sizeof(SAFER_IDENTIFICATION_HEADER));
|
|
pCommon->cbStructSize = sizeof(SAFER_IDENTIFICATION_HEADER);
|
|
RtlCopyMemory(&pCommon->IdentificationGuid,
|
|
&pAuthzLevelStruct->identGuid,
|
|
sizeof(GUID));
|
|
}
|
|
else
|
|
{
|
|
// Query information about a specific, existing GUID.
|
|
Status = __CodeAuthzpQuerySingleIdentification(
|
|
pSingleIdentRecord,
|
|
lpQueryBuffer,
|
|
dwInBufferSize,
|
|
lpdwOutBufferSize
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto ExitHandler;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ASSERTMSG("all info classes were not handled", 0);
|
|
Status = STATUS_INVALID_INFO_CLASS;
|
|
goto ExitHandler;
|
|
}
|
|
Status = STATUS_SUCCESS;
|
|
|
|
|
|
|
|
//
|
|
// Cleanup and epilogue code.
|
|
//
|
|
|
|
ExitHandler:
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
BOOL WINAPI
|
|
SaferGetLevelInformation(
|
|
IN SAFER_LEVEL_HANDLE LevelHandle,
|
|
IN SAFER_OBJECT_INFO_CLASS dwInfoType,
|
|
OUT LPVOID lpQueryBuffer OPTIONAL ,
|
|
IN DWORD dwInBufferSize,
|
|
OUT LPDWORD lpdwOutBufferSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allows the user to query various pieces of information about a
|
|
given AuthzObject handle.
|
|
|
|
Arguments:
|
|
|
|
LevelHandle - the handle to the authorization object to evaluate.
|
|
|
|
dwInfoType - specifies the type of information being requested.
|
|
|
|
lpQueryBuffer - pointer to a user-supplied memory buffer that
|
|
will receive the requested information.
|
|
|
|
dwInBufferSize - specifies the size of the user's memory block.
|
|
|
|
lpdwOutBufferSize - receives the used size of the data within the
|
|
memory block, or the minimum necessary size if the passed
|
|
buffer was too small.
|
|
|
|
Return Value:
|
|
|
|
Returns FALSE on error, otherwise success.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
|
|
if (!g_bInitializedFirstTime) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto ExitHandler;
|
|
}
|
|
|
|
if (!ARGUMENT_PRESENT(lpdwOutBufferSize)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto ExitHandler;
|
|
}
|
|
|
|
RtlEnterCriticalSection(&g_TableCritSec);
|
|
|
|
Status = __CodeAuthzpGetAuthzLevelInfo(
|
|
LevelHandle, dwInfoType,
|
|
lpQueryBuffer, dwInBufferSize,
|
|
lpdwOutBufferSize);
|
|
|
|
RtlLeaveCriticalSection(&g_TableCritSec);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
return TRUE;
|
|
|
|
ExitHandler:
|
|
BaseSetLastNTError(Status);
|
|
return FALSE;
|
|
}
|