Skip to content

Commit

Permalink
Improved Merge operation (#484)
Browse files Browse the repository at this point in the history
After adding support for signed App Control policies to the AppControl Manager app, now adding support for Supplemental policy signers and Update policy signers to the merge functions.

Improved the merge functions further to consider the signing scenario during deduplication of FileRules.

Fixed a bug in Merge operation related to WHQLFilePublisher and WHQLPublisher level deduplication, their IDs were being considered instead of value.

The Merge button now stays disabled while the merge operation is running.

Added support for AllowAll/DenyAll FileRules in the merge functions.
  • Loading branch information
HotCakeX authored Dec 26, 2024
1 parent 8adac00 commit 50d8b4f
Show file tree
Hide file tree
Showing 12 changed files with 443 additions and 61 deletions.
15 changes: 13 additions & 2 deletions AppControl Manager/Pages/MergePolicies.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ public MergePolicies()
this.NavigationCacheMode = NavigationCacheMode.Enabled;
}


/// <summary>
/// Event handler for the main Merge button
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void MergeButton_Click(object sender, RoutedEventArgs e)
{

Expand Down Expand Up @@ -53,6 +59,8 @@ private async void MergeButton_Click(object sender, RoutedEventArgs e)
try
{

MergeButton.IsEnabled = false;

PolicyMergerInfoBar.IsOpen = true;

PolicyMergerInfoBar.Message = "Merging the policies";
Expand Down Expand Up @@ -113,6 +121,9 @@ await Task.Run(() =>
PolicyMergerInfoBar.IsClosable = true;

MergeProgressRing.Visibility = Visibility.Collapsed;


MergeButton.IsEnabled = true;
}
}

Expand All @@ -132,7 +143,7 @@ private void MainPolicyBrowseButton_Click(object sender, RoutedEventArgs e)
mainPolicy = selectedFile;

// Add the selected main XML policy file path to the flyout's TextBox
MainPolicy_Flyout_TextBox.Text += selectedFile;
MainPolicy_Flyout_TextBox.Text = selectedFile;
}
}

Expand All @@ -147,7 +158,7 @@ private void MainPolicySettingsCard_Click(object sender, RoutedEventArgs e)
mainPolicy = selectedFile;

// Add the selected main XML policy file path to the flyout's TextBox
MainPolicy_Flyout_TextBox.Text += selectedFile;
MainPolicy_Flyout_TextBox.Text = selectedFile;
}

// Manually display the Flyout since user clicked/tapped on the Settings card and not the button itself
Expand Down
120 changes: 117 additions & 3 deletions AppControl Manager/SiPolicy/Merger.Aggregate.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using AppControlManager.Logging;
using AppControlManager.SiPolicyIntel;

namespace AppControlManager.SiPolicy;
Expand Down Expand Up @@ -146,6 +147,8 @@ internal static SignerCollection CollectSignerRules(List<SiPolicy> siPolicies)
HashSet<SignerRule> signerRules = new(new PublisherSignerRuleComparer());
HashSet<WHQLFilePublisher> whqlFilePublishers = new(new WHQLFilePublisherSignerRuleComparer());
HashSet<WHQLPublisher> wHQLPublishers = new(new WHQLPublisherSignerRuleComparer());
HashSet<UpdatePolicySignerRule> updatePolicySignerRules = new(new UpdatePolicySignerRuleComparer());
HashSet<SupplementalPolicySignerRule> supplementalPolicySignerRules = new(new SupplementalPolicySignerRuleComparer());

// Loop over each policy input data
foreach (SiPolicy siPolicy in siPolicies)
Expand All @@ -156,15 +159,37 @@ internal static SignerCollection CollectSignerRules(List<SiPolicy> siPolicies)
.ToDictionary(fileAttrib => fileAttrib.ID, fileAttrib => fileAttrib);

// Get all of the <Signer> elements from the policy
Dictionary<string, Signer> signerDictionary = siPolicy.Signers
.ToDictionary(signer => signer.ID, signer => signer);
Dictionary<string, Signer> signerDictionary = [];

foreach (Signer signer in siPolicy.Signers)
{
if (!signerDictionary.TryAdd(signer.ID, signer))
{
Logger.Write($"One of the XML files has more than 1 Signer with the same ID `{signer.ID}`");
}
}


// ID of all of the CiSigners if they exist
HashSet<string> ciSignerSet = [.. siPolicy.CiSigners?.Select(ciSigner => ciSigner.SignerId) ?? []];

// Dictionary to store all of the EKUs
Dictionary<string, EKU> ekuDictionary = siPolicy.EKUs?.ToDictionary(eku => eku.ID, eku => eku) ?? [];

// ID of all of the SupplementalPolicySigners if they exist
HashSet<string> supplementalPolicySignersSet = [.. siPolicy.SupplementalPolicySigners?.Select(supplementalPolicySigner => supplementalPolicySigner.SignerId) ?? []];

// ID of all of the UpdatePolicySigners if they exist
HashSet<string> updatePolicySignersSet = [.. siPolicy.UpdatePolicySigners?.Select(updatePolicySigner => updatePolicySigner.SignerId) ?? []];


// Collecting UpdatePolicySigners and SupplementalPolicySigners separately
// Because they are not part of any SigningScenario and don't have Allowed/Denied signers
ProcessSupplementalPolicySigners(supplementalPolicySignersSet, signerDictionary, supplementalPolicySignerRules);

ProcessUpdatePolicySigners(updatePolicySignersSet, signerDictionary, updatePolicySignerRules);


// Step 2: Process SigningScenarios
foreach (SigningScenario signingScenario in siPolicy.SigningScenarios)
{
Expand Down Expand Up @@ -234,7 +259,9 @@ internal static SignerCollection CollectSignerRules(List<SiPolicy> siPolicies)
FilePublisherSigners = filePublisherSigners,
SignerRules = signerRules,
WHQLPublishers = wHQLPublishers,
WHQLFilePublishers = whqlFilePublishers
WHQLFilePublishers = whqlFilePublishers,
UpdatePolicySigners = updatePolicySignerRules,
SupplementalPolicySigners = supplementalPolicySignerRules
};
}

Expand Down Expand Up @@ -560,4 +587,91 @@ private static void AddSignerRule(
}
}



/// <summary>
/// Processes SupplementalPolicySigners
/// </summary>
/// <param name="supplementalPolicySignerIDs"></param>
/// <param name="Signers"></param>
/// <param name="supplementalPolicySignersSet"></param>
private static void ProcessSupplementalPolicySigners(
HashSet<string> supplementalPolicySignerIDs,
Dictionary<string, Signer> Signers,
HashSet<SupplementalPolicySignerRule> supplementalPolicySignersSet)
{

foreach (string ID in supplementalPolicySignerIDs)
{
if (Signers.TryGetValue(ID, out Signer? possibleSupplementalPolicySigner))
{

// Create random ID for the signer and its corresponding SupplementalPolicySigner element
string guid = GUIDGenerator.GenerateUniqueGUIDToUpper();
string rand = $"ID_SIGNER_A_{guid}";

// Replace the Signer's ID
possibleSupplementalPolicySigner.ID = rand;


// Create a new SupplementalPolicySigner element with the new ID
SupplementalPolicySigner suppRule = new()
{
SignerId = rand
};


_ = supplementalPolicySignersSet.Add(new SupplementalPolicySignerRule
{
SignerElement = possibleSupplementalPolicySigner,
SupplementalPolicySigner = suppRule
});
}
}
}




/// <summary>
/// Processes UpdatePolicySigners
/// </summary>
/// <param name="updatePolicySignerIDs"></param>
/// <param name="Signers"></param>
/// <param name="updatePolicySignersSet"></param>
private static void ProcessUpdatePolicySigners(
HashSet<string> updatePolicySignerIDs,
Dictionary<string, Signer> Signers,
HashSet<UpdatePolicySignerRule> updatePolicySignersSet)
{

foreach (string ID in updatePolicySignerIDs)
{
if (Signers.TryGetValue(ID, out Signer? possibleUpdatePolicySigner))
{

// Create random ID for the signer and its corresponding UpdatePolicySigner element
string guid = GUIDGenerator.GenerateUniqueGUIDToUpper();
string rand = $"ID_SIGNER_A_{guid}";

// Replace the Signer's ID
possibleUpdatePolicySigner.ID = rand;

// Create a new UpdatePolicySigner element with the new ID
UpdatePolicySigner uppRule = new()
{
SignerId = rand
};

_ = updatePolicySignersSet.Add(new UpdatePolicySignerRule
{
SignerElement = possibleUpdatePolicySigner,
UpdatePolicySigner = uppRule
});
}
}


}

}
39 changes: 23 additions & 16 deletions AppControl Manager/SiPolicy/Merger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ internal static void Merge(string mainXmlFilePath, HashSet<string> otherXmlFileP
IEnumerable<Signer> signers = signerCollection.FilePublisherSigners.Select(x => x.SignerElement).
Concat(signerCollection.WHQLFilePublishers.Select(x => x.SignerElement)).
Concat(signerCollection.WHQLPublishers.Select(x => x.SignerElement)).
Concat(signerCollection.SignerRules.Select(x => x.SignerElement));
Concat(signerCollection.SignerRules.Select(x => x.SignerElement)).
Concat(signerCollection.SupplementalPolicySigners.Select(x => x.SignerElement)).
Concat(signerCollection.UpdatePolicySigners.Select(x => x.SignerElement));

// Get all CiSigners
IEnumerable<CiSigner> ciSigners = signerCollection.WHQLPublishers.Where(x => x.SigningScenario is SSType.UserMode).Select(x => x.CiSignerElement!).
Expand Down Expand Up @@ -130,6 +132,11 @@ internal static void Merge(string mainXmlFilePath, HashSet<string> otherXmlFileP
Concat(signerCollection.FilePublisherSigners.Where(x => x.SigningScenario is SSType.KernelMode && x.Auth is Authorization.Deny).Select(x => x.DeniedSignerElement))!;


IEnumerable<SupplementalPolicySigner> supplementalPolicySignersCol = signerCollection.SupplementalPolicySigners.Where(x => x is not null).Select(x => x.SupplementalPolicySigner);

IEnumerable<UpdatePolicySigner> updatePolicySignersCol = signerCollection.UpdatePolicySigners.Where(x => x is not null).Select(x => x.UpdatePolicySigner);


// Construct the User Mode Signing Scenario
SigningScenario UMCISigningScenario = new()
{
Expand Down Expand Up @@ -207,27 +214,27 @@ internal static void Merge(string mainXmlFilePath, HashSet<string> otherXmlFileP
// Create the final policy data, it will replace the content in the main XML file
SiPolicy output = new()
{
VersionEx = mainXML.VersionEx, // Main policy takes priority
PolicyTypeID = mainXML.PolicyTypeID, // Main policy takes priority
PlatformID = mainXML.PlatformID, // Main policy takes priority
PolicyID = mainXML.PolicyID, // Main policy takes priority
BasePolicyID = mainXML.BasePolicyID, // Main policy takes priority
Rules = mainXML.Rules, // Main policy takes priority
VersionEx = mainXML.VersionEx, // Main policy takes priority
PolicyTypeID = mainXML.PolicyTypeID, // Main policy takes priority
PlatformID = mainXML.PlatformID, // Main policy takes priority
PolicyID = mainXML.PolicyID, // Main policy takes priority
BasePolicyID = mainXML.BasePolicyID, // Main policy takes priority
Rules = mainXML.Rules, // Main policy takes priority
EKUs = [.. ekusToUse], // Aggregated data
FileRules = [.. fileRules], // Aggregated data
Signers = [.. signers], // Aggregated data
Signers = [.. signers], // Aggregated data
SigningScenarios = [UMCISigningScenario, KMCISigningScenario], // Aggregated data
UpdatePolicySigners = mainXML.UpdatePolicySigners, // Main policy takes priority
UpdatePolicySigners = [.. updatePolicySignersCol], // Aggregated data
CiSigners = [.. ciSigners], // Aggregated data
HvciOptions = 2, // Set to the secure state
HvciOptionsSpecified = true, // Set to the secure state
Settings = mainXML.Settings, // Main policy takes priority
Macros = mainXML.Macros, // Main policy takes priority
SupplementalPolicySigners = mainXML.SupplementalPolicySigners, // Main policy takes priority
AppSettings = mainXML.AppSettings, // Main policy takes priority
FriendlyName = mainXML.FriendlyName, // Main policy takes priority
PolicyType = mainXML.PolicyType, // Main policy takes priority
PolicyTypeSpecified = mainXML.PolicyTypeSpecified // Main policy takes priority
Settings = mainXML.Settings, // Main policy takes priority
Macros = mainXML.Macros, // Main policy takes priority
SupplementalPolicySigners = [.. supplementalPolicySignersCol], // Aggregated data
AppSettings = mainXML.AppSettings, // Main policy takes priority
FriendlyName = mainXML.FriendlyName, // Main policy takes priority
PolicyType = mainXML.PolicyType, // Main policy takes priority
PolicyTypeSpecified = mainXML.PolicyTypeSpecified // Main policy takes priority
};


Expand Down
12 changes: 12 additions & 0 deletions AppControl Manager/SiPolicyIntel/AllowRuleComparer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ public bool Equals(AllowRule? x, AllowRule? y)
return false;
}

// Check SSType
if (x.SigningScenario != y.SigningScenario)
{
return false;
}

Allow allowX = x.AllowElement;
Allow allowY = y.AllowElement;

Expand All @@ -38,6 +44,12 @@ public bool Equals(AllowRule? x, AllowRule? y)
return true;
}

// Rule special case: Check if FileName is "*" in both and are equal
if (string.Equals(allowX.FileName, "*", StringComparison.OrdinalIgnoreCase) && string.Equals(allowY.FileName, "*", StringComparison.OrdinalIgnoreCase))
{
return true;
}

// Rule 4: Check for MinimumFileVersion or MaximumFileVersion and the other properties
bool hasMinX = !string.IsNullOrWhiteSpace(allowX.MinimumFileVersion);
bool hasMaxX = !string.IsNullOrWhiteSpace(allowX.MaximumFileVersion);
Expand Down
12 changes: 12 additions & 0 deletions AppControl Manager/SiPolicyIntel/DenyRuleComparer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ public bool Equals(DenyRule? x, DenyRule? y)
return false;
}

// Check SSType
if (x.SigningScenario != y.SigningScenario)
{
return false;
}

Deny denyX = x.DenyElement;
Deny denyY = y.DenyElement;

Expand All @@ -38,6 +44,12 @@ public bool Equals(DenyRule? x, DenyRule? y)
return true;
}

// Rule special case: Check if FileName is "*" in both and are equal
if (string.Equals(denyX.FileName, "*", StringComparison.OrdinalIgnoreCase) && string.Equals(denyX.FileName, "*", StringComparison.OrdinalIgnoreCase))
{
return true;
}

// Rule 4: Check for MinimumFileVersion or MaximumFileVersion and the other properties
bool hasMinX = !string.IsNullOrWhiteSpace(denyX.MinimumFileVersion);
bool hasMaxX = !string.IsNullOrWhiteSpace(denyX.MaximumFileVersion);
Expand Down
2 changes: 2 additions & 0 deletions AppControl Manager/SiPolicyIntel/SignerCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ internal sealed class SignerCollection
internal required HashSet<SignerRule> SignerRules { get; set; }
internal required HashSet<WHQLPublisher> WHQLPublishers { get; set; }
internal required HashSet<WHQLFilePublisher> WHQLFilePublishers { get; set; }
internal required HashSet<UpdatePolicySignerRule> UpdatePolicySigners { get; set; }
internal required HashSet<SupplementalPolicySignerRule> SupplementalPolicySigners { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using AppControlManager.SiPolicy;

namespace AppControlManager.SiPolicyIntel;

internal sealed class SupplementalPolicySignerRule
{
internal required Signer SignerElement { get; set; }
internal required SupplementalPolicySigner SupplementalPolicySigner { get; set; }
}
Loading

0 comments on commit 50d8b4f

Please sign in to comment.