/*++ 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 #include #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; }