Skip to content

Commit

Permalink
Add tests for configure mixed elevation (#4487)
Browse files Browse the repository at this point in the history
  • Loading branch information
ryfu-msft committed Jun 14, 2024
1 parent 06ac471 commit 8503a86
Show file tree
Hide file tree
Showing 36 changed files with 411 additions and 37 deletions.
8 changes: 4 additions & 4 deletions .github/actions/spelling/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ pseudocode
PSHOST
psobject
ptstr
publickey
publickey
PVD
pvk
pvm
Expand Down Expand Up @@ -413,8 +413,8 @@ servercert
servercertificate
setmetadatabymanifestid
SETTINGCHANGE
SETTINGMAPPING
sfs
SETTINGMAPPING
sfs
sfsclient
SHCONTF
SHGDN
Expand All @@ -436,7 +436,7 @@ Srinivasan
srs
startswith
STARTUPINFOW
STDMETHODCALLTYPE
STDMETHODCALLTYPE
storeapps
storeorigin
STRRET
Expand Down
1 change: 1 addition & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ jobs:
testSelector: 'testAssemblies'
testAssemblyVer2: '**\Microsoft.Management.Configuration.UnitTests.dll'
searchFolder: '$(buildOutDir)\Microsoft.Management.Configuration.UnitTests'
testFiltercriteria: 'Category=InProc'
codeCoverageEnabled: false
platform: '$(buildPlatform)'
configuration: '$(BuildConfiguration)'
Expand Down
65 changes: 60 additions & 5 deletions src/AppInstallerCLICore/ConfigurationDynamicRuntimeFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,28 @@ namespace AppInstaller::CLI::ConfigurationRemoting
{
namespace anonymous
{
#ifndef AICLI_DISABLE_TEST_HOOKS
constexpr std::wstring_view EnableTestModeTestGuid = L"1e62d683-2999-44e7-81f7-6f8f35e8d731";
constexpr std::wstring_view ForceHighIntegrityLevelUnitsTestGuid = L"f698d20f-3584-4f28-bc75-28037e08e651";
constexpr std::wstring_view EnableRestrictedIntegrityLevelTestGuid = L"5cae3226-185f-4289-815c-3c089d238dc6";

// Checks the configuration set metadata for a specific test guid that controls the behavior flow.
bool GetConfigurationSetMetadataOverride(const ConfigurationSet& configurationSet, const std::wstring_view& testGuid)
{
auto metadataOverride = configurationSet.Metadata().TryLookup(testGuid);
if (metadataOverride)
{
auto metadataOverrideProperty = metadataOverride.try_as<IPropertyValue>();
if (metadataOverrideProperty && metadataOverrideProperty.Type() == PropertyType::Boolean)
{
return metadataOverrideProperty.GetBoolean();
}
}

return false;
}
#endif

struct DynamicProcessorInfo
{
IConfigurationSetProcessorFactory Factory;
Expand All @@ -26,7 +48,16 @@ namespace AppInstaller::CLI::ConfigurationRemoting

DynamicSetProcessor(IConfigurationSetProcessorFactory defaultRemoteFactory, IConfigurationSetProcessor defaultRemoteSetProcessor, const ConfigurationSet& configurationSet) : m_configurationSet(configurationSet)
{
#ifndef AICLI_DISABLE_TEST_HOOKS
m_enableTestMode = GetConfigurationSetMetadataOverride(m_configurationSet, EnableTestModeTestGuid);
m_enableRestrictedIntegrityLevel = GetConfigurationSetMetadataOverride(m_configurationSet, EnableRestrictedIntegrityLevelTestGuid);
m_forceHighIntegrityLevelUnits = GetConfigurationSetMetadataOverride(m_configurationSet, ForceHighIntegrityLevelUnitsTestGuid);

m_currentIntegrityLevel = m_enableTestMode ? Security::IntegrityLevel::Medium : Security::GetEffectiveIntegrityLevel();
#else
m_currentIntegrityLevel = Security::GetEffectiveIntegrityLevel();
#endif

m_setProcessors.emplace(m_currentIntegrityLevel, DynamicProcessorInfo{ defaultRemoteFactory, defaultRemoteSetProcessor });
}

Expand Down Expand Up @@ -56,7 +87,11 @@ namespace AppInstaller::CLI::ConfigurationRemoting
});

// Create set and unit processor for current unit.
#ifndef AICLI_DISABLE_TEST_HOOKS
Security::IntegrityLevel requiredIntegrityLevel = m_forceHighIntegrityLevelUnits ? Security::IntegrityLevel::High : GetIntegrityLevelForUnit(unit);
#else
Security::IntegrityLevel requiredIntegrityLevel = GetIntegrityLevelForUnit(unit);
#endif

auto itr = m_setProcessors.find(requiredIntegrityLevel);
if (itr == m_setProcessors.end())
Expand All @@ -79,11 +114,20 @@ namespace AppInstaller::CLI::ConfigurationRemoting
}
else if (securityContextLower == L"restricted")
{
// Not supporting elevated callers downgrading at the moment.
THROW_WIN32(ERROR_NOT_SUPPORTED);
#ifndef AICLI_DISABLE_TEST_HOOKS
if (m_enableRestrictedIntegrityLevel)
{
return Security::IntegrityLevel::Medium;
}
else
#endif
{
// Not supporting elevated callers downgrading at the moment.
THROW_WIN32(ERROR_NOT_SUPPORTED);

// Technically this means the default level of the user token, so if UAC is disabled it would be the only integrity level (aka current).
//return Security::IntegrityLevel::Medium;
// Technically this means the default level of the user token, so if UAC is disabled it would be the only integrity level (aka current).
// return Security::IntegrityLevel::Medium;
}
}
else if (securityContextLower == L"current")
{
Expand Down Expand Up @@ -172,7 +216,12 @@ namespace AppInstaller::CLI::ConfigurationRemoting
// If we got here, the only option is that the current integrity level is not High.
if (integrityLevel == Security::IntegrityLevel::High)
{
factory = CreateOutOfProcessFactory(true, SerializeSetProperties(), SerializeHighIntegrityLevelSet());
bool useRunAs = true;
#ifndef AICLI_DISABLE_TEST_HOOKS
useRunAs = !m_enableTestMode;
#endif

factory = CreateOutOfProcessFactory(useRunAs, SerializeSetProperties(), SerializeHighIntegrityLevelSet());
}
else
{
Expand All @@ -186,6 +235,12 @@ namespace AppInstaller::CLI::ConfigurationRemoting
ProcessorMap m_setProcessors;
ConfigurationSet m_configurationSet;
std::once_flag m_createUnitSetProcessorsOnce;

#ifndef AICLI_DISABLE_TEST_HOOKS
bool m_enableTestMode = false;
bool m_enableRestrictedIntegrityLevel = false;
bool m_forceHighIntegrityLevelUnits = false;
#endif
};

// This is implemented completely in the packaged context for now, if we want to make it more configurable, we will probably want to move it to configuration and
Expand Down
3 changes: 1 addition & 2 deletions src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,7 @@ namespace AppInstaller::CLI::Workflow
IConfigurationSetProcessorFactory factory;

// Since downgrading is not currently supported, only use dynamic if not running as admin.
if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::ConfigureSelfElevation) &&
!Runtime::IsRunningAsAdmin())
if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::ConfigureSelfElevation) && !Runtime::IsRunningAsAdmin())
{
factory = ConfigurationRemoting::CreateDynamicRuntimeFactory();
// TODO: Implement SetProcessorFactory::IPwshConfigurationSetProcessorFactoryProperties on dynamic factory
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#
#
# Module manifest for module 'xE2ETestResource'
#

Expand All @@ -22,6 +22,7 @@ DscResourcesToExport = @(
'E2ETestResourceError'
'E2ETestResourceTypes'
'E2ETestResourceCrash'
'E2ETestResourcePID'
)
HelpInfoURI = 'https://www.contoso.com/help'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# E2E module with resources.
# E2E module with resources.

enum Ensure
{
Expand Down Expand Up @@ -288,3 +288,39 @@ class E2ETestResourceCrash
[System.Environment]::Exit(0)
}
}

# This resource writes the current PID to the provided file path.
[DscResource()]
class E2ETestResourcePID
{
[DscProperty(Key)]
[string] $key

[DscProperty(Mandatory)]
[string] $directoryPath

[E2ETestResourcePID] Get()
{
$result = @{
key = "E2ETestResourcePID"
directoryPath = $this.directoryPath
}

return $result
}

[bool] Test()
{
return $false
}

[void] Set()
{
if (Test-Path -Path $this.directoryPath)
{
$processId = [System.Diagnostics.Process]::GetCurrentProcess().Id
$filePath = Join-Path -Path $this.directoryPath -ChildPath "$processId.txt"
New-Item -Path $filePath -ItemType File -Force
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ namespace Microsoft.Management.Configuration.Processor
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Management.Automation;
using System.Runtime.CompilerServices;
using System.Text;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// <copyright file="Constants.cs" company="Microsoft Corporation">
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
// </copyright>
Expand All @@ -20,5 +20,25 @@ public class Constants
/// The namespace where xUnit traits will be defined.
/// </summary>
public const string NamespaceNameForTraits = "Microsoft.Management.Configuration.UnitTests.Helpers";

/// <summary>
/// The dynamic runtime factory handler identifier.
/// </summary>
public const string DynamicRuntimeHandlerIdentifier = "{73fea39f-6f4a-41c9-ba94-6fd14d633e40}";

/// <summary>
/// Test guid for enabling test mode for the dynamic runtime factory. Forces factory to exclude 'runas' verb and sets current IL to medium.
/// </summary>
public const string EnableDynamicFactoryTestMode = "1e62d683-2999-44e7-81f7-6f8f35e8d731";

/// <summary>
/// Test guid for allowing the restricted integrity level to be supported.
/// </summary>
public const string EnableRestrictedIntegrityLevelTestGuid = "5cae3226-185f-4289-815c-3c089d238dc6";

/// <summary>
/// Test guid for forcing units to have a high integrity level during the final routing of unit processor creation.
/// </summary>
public const string ForceHighIntegrityLevelUnitsTestGuid = "f698d20f-3584-4f28-bc75-28037e08e651";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ internal static class Errors
public static readonly int WINGET_CONFIG_ERROR_UNIT_IMPORT_MODULE_ADMIN = unchecked((int)0x8A15C111);
public static readonly int WINGET_CONFIG_ERROR_NOT_SUPPORTED_BY_PROCESSOR = unchecked((int)0x8A15C112);

// Limitation Set Errors
public static readonly int CORE_INVALID_OPERATION = unchecked((int)0x80131509);

#pragma warning restore SA1025 // Code should not contain multiple whitespace in a row
#pragma warning restore SA1600 // Elements should be documented
#pragma warning restore SA1310 // Field names should not contain underscore
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// -----------------------------------------------------------------------------
// <copyright file="InProcAttribute.cs" company="Microsoft Corporation">
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
// </copyright>
// -----------------------------------------------------------------------------

namespace Microsoft.Management.Configuration.UnitTests.Helpers
{
using System;
using Xunit.Sdk;

/// <summary>
/// Trait used to mark a test as only for the in proc scenario.
/// </summary>
[TraitDiscoverer(InProcDiscoverer.TypeName, Constants.AssemblyNameForTraits)]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class InProcAttribute : Attribute, ITraitAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="InProcAttribute"/> class.
/// </summary>
public InProcAttribute()
{
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// -----------------------------------------------------------------------------
// <copyright file="InProcDiscoverer.cs" company="Microsoft Corporation">
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
// </copyright>
// -----------------------------------------------------------------------------

namespace Microsoft.Management.Configuration.UnitTests.Helpers
{
using System.Collections.Generic;
using Xunit.Abstractions;
using Xunit.Sdk;

/// <summary>
/// Enables integration with xUnit trait system.
/// </summary>
public class InProcDiscoverer : ITraitDiscoverer
{
/// <summary>
/// The type name for this discoverer.
/// </summary>
public const string TypeName = Constants.NamespaceNameForTraits + ".InProcDiscoverer";

/// <summary>
/// Initializes a new instance of the <see cref="InProcDiscoverer"/> class.
/// </summary>
public InProcDiscoverer()
{
}

/// <summary>
/// Gets the trait information for the InProcAttribute.
/// </summary>
/// <param name="traitAttribute">The trait information.</param>
/// <returns>Trait name/value pairs.</returns>
public IEnumerable<KeyValuePair<string, string>> GetTraits(IAttributeInfo traitAttribute)
{
yield return new KeyValuePair<string, string>("Category", "InProc");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// <copyright file="ConfigurationDetailsTests.cs" company="Microsoft Corporation">
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
// </copyright>
Expand All @@ -14,6 +14,7 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests
using Microsoft.Management.Configuration.Processor.Helpers;
using Microsoft.Management.Configuration.Processor.Unit;
using Microsoft.Management.Configuration.UnitTests.Fixtures;
using Microsoft.Management.Configuration.UnitTests.Helpers;
using Windows.Security.Cryptography.Certificates;
using Xunit;
using Xunit.Abstractions;
Expand All @@ -22,6 +23,7 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests
/// Tests for ConfigurationUnitProcessorDetails and ConfigurationUnitSettingDetails.
/// </summary>
[Collection("UnitTestCollection")]
[InProc]
public class ConfigurationDetailsTests
{
private readonly UnitTestFixture fixture;
Expand Down
Loading

0 comments on commit 8503a86

Please sign in to comment.