diff --git a/TameMyCerts.Tests/XMLPolicyTests.cs b/TameMyCerts.Tests/XMLPolicyTests.cs
index f943362..9f7bda0 100644
--- a/TameMyCerts.Tests/XMLPolicyTests.cs
+++ b/TameMyCerts.Tests/XMLPolicyTests.cs
@@ -70,7 +70,7 @@ public void Test_Unknown_XML_Element()
Assert.Empty(cacheEntry.ErrorMessage);
Assert.Equal(2, _listener.Events.Count);
Assert.Equal(92, _listener.Events[0].EventId);
-
+ output.WriteLine(_listener.Events[0].Message);
File.Delete(filename);
}
@@ -96,4 +96,53 @@ public void Test_Unknown_XML_Element2()
File.Delete(filename);
}
+ [Fact]
+ public void Test_Yubikey_Policies()
+ {
+ var filename = Path.GetTempFileName();
+
+ string sampleXML = @"
+
+
+ Allow
+
+ 9A
+
+
+
+
+
+";
+ File.WriteAllText(filename, sampleXML);
+ _listener.ClearEvents();
+
+ CertificateRequestPolicyCacheEntry cacheEntry = new CertificateRequestPolicyCacheEntry(filename);
+
+ //Assert.Empty(cacheEntry.ErrorMessage);
+ //Assert.Equal(2, _listener.Events.Count);
+ Assert.DoesNotContain(92, _listener.Events.Select(e => e.EventId));
+ File.Delete(filename);
+ }
+
+
+ [Fact]
+ public void Broken_XML_Policies()
+ {
+ var filename = Path.GetTempFileName();
+
+ string sampleXML = @"
+
+";
+ File.WriteAllText(filename, sampleXML);
+ _listener.ClearEvents();
+
+ CertificateRequestPolicyCacheEntry cacheEntry = new CertificateRequestPolicyCacheEntry(filename);
+
+ output.WriteLine(cacheEntry.ErrorMessage);
+ Assert.Contains(94, _listener.Events.Select(e => e.EventId));
+ File.Delete(filename);
+ }
+
}
\ No newline at end of file
diff --git a/TameMyCerts.Tests/YubikeyValidatorTests.cs b/TameMyCerts.Tests/YubikeyValidatorTests.cs
index b12dbf5..a342348 100644
--- a/TameMyCerts.Tests/YubikeyValidatorTests.cs
+++ b/TameMyCerts.Tests/YubikeyValidatorTests.cs
@@ -576,7 +576,7 @@ public void Rewrite_Subject_to_slot_10014()
}
);
- result = _YKvalidator.VerifyRequest(result, policy, yubikeyInfo, 10014);
+ result = _YKvalidator.VerifyRequest(result, policy, yubikeyInfo, dbRow.RequestID);
result = _CCvalidator.VerifyRequest(result, policy, _yubikey_valid_5_4_3_Once_Never_UsbAKeychain_9a_Normal_RSA_2048_dbRow, null, _caConfig, yubikeyInfo);
PrintResult(result);
@@ -637,7 +637,7 @@ public void Validate_Accutial_Attestions_certificate_wrong_public_key_10015()
result = _YKvalidator.ExtractAttestion(result, _policy, dbrow, out var yubikey);
CertificateRequestPolicy policy = _policy;
- result = _YKvalidator.VerifyRequest(result, policy, yubikey, 10015);
+ result = _YKvalidator.VerifyRequest(result, policy, yubikey, dbrow.RequestID);
PrintResult(result);
Assert.Contains(4207, _listener.Events.Select(e => e.EventId));
@@ -655,12 +655,126 @@ public void Include_the_AttestionData_in_Certificate_10016()
CertificateRequestPolicy policy = _policy;
policy.YubikeyPolicy[0].IncludeAttestationInCertificate = true;
- result = _YKvalidator.VerifyRequest(result, policy, yubikeyInfo, 10016);
+ result = _YKvalidator.VerifyRequest(result, policy, yubikeyInfo, dbRow.RequestID);
PrintResult(result);
Assert.False(result.DeniedForIssuance);
Assert.True(result.CertificateExtensions.ContainsKey(YubikeyX509Extensions.ATTESTION_DEVICE));
+
+ }
+
+ [Fact]
+ public void Validate_Slot_Allow_policy_10017()
+ {
+ CertificateDatabaseRow dbRow = new CertificateDatabaseRow(_yubikey_valid_5_4_3_Once_Never_UsbAKeychain_9a_Normal_RSA_2048_CSR, CertCli.CR_IN_PKCS10, null, 10017);
+
+ // Allow if Slot is in an allow Policy
+ CertificateRequestPolicy policy = _policy;
+ policy.YubikeyPolicy[0].Slot = new List { "9a" };
+ var result = new CertificateRequestValidationResult(dbRow);
+ result = _YKvalidator.ExtractAttestion(result, _policy, dbRow, out var yubikeyInfo);
+ result = _YKvalidator.VerifyRequest(result, policy, yubikeyInfo, dbRow.RequestID);
+
+ PrintResult(result);
+
+ Assert.False(result.DeniedForIssuance);
+
+ }
+
+ [Fact]
+ public void Validate_Slot_Deny_policy_10018()
+ {
+ CertificateDatabaseRow dbRow = new CertificateDatabaseRow(_yubikey_valid_5_4_3_Once_Never_UsbAKeychain_9a_Normal_RSA_2048_CSR, CertCli.CR_IN_PKCS10, null, 10018);
+
+ // Deny if Slot is in a deny Policy
+ CertificateRequestPolicy policy = _policy;
+ policy.YubikeyPolicy[0].Slot = new List { "9a" };
+ policy.YubikeyPolicy[0].Action = YubikeyPolicyAction.Deny;
+ var result = new CertificateRequestValidationResult(dbRow);
+ result = _YKvalidator.ExtractAttestion(result, _policy, dbRow, out var yubikeyInfo);
+ result = _YKvalidator.VerifyRequest(result, policy, yubikeyInfo, dbRow.RequestID);
+
+ PrintResult(result);
+
+ Assert.True(result.DeniedForIssuance);
+
+ }
+
+ [Fact]
+ public void Validate_Slot_Missing_in_Allow_policy_10019()
+ {
+ CertificateDatabaseRow dbRow = new CertificateDatabaseRow(_yubikey_valid_5_4_3_Once_Never_UsbAKeychain_9a_Normal_RSA_2048_CSR, CertCli.CR_IN_PKCS10, null, 10019);
+ CertificateRequestPolicy policy = _policy;
+ var result = new CertificateRequestValidationResult(dbRow);
+
+ // Test if the slot is not in the only allow policy
+ policy = _policy;
+ policy.YubikeyPolicy[0].Slot = new List { "9e" };
+ result = new CertificateRequestValidationResult(dbRow);
+ result = _YKvalidator.ExtractAttestion(result, _policy, dbRow, out var yubikeyInfo);
+ result = _YKvalidator.VerifyRequest(result, policy, yubikeyInfo, dbRow.RequestID);
+ Assert.True(result.DeniedForIssuance);
+
+ PrintResult(result);
+
+ }
+
+ [Fact]
+ public void Validate_Slot_Allow_if_Wrong_slot_is_denied_10020()
+ {
+ CertificateDatabaseRow dbRow = new CertificateDatabaseRow(_yubikey_valid_5_4_3_Once_Never_UsbAKeychain_9a_Normal_RSA_2048_CSR, CertCli.CR_IN_PKCS10, null, 10020);
+ CertificateRequestPolicy policy = _policy;
+ var result = new CertificateRequestValidationResult(dbRow);
+
+ // Allow if the Deny does not match this slot
+ policy = _policy;
+ policy.YubikeyPolicy.Add(new YubikeyPolicy());
+ policy.YubikeyPolicy[0].Action = YubikeyPolicyAction.Deny;
+ policy.YubikeyPolicy[0].Slot = new List { "9e" };
+ result = new CertificateRequestValidationResult(dbRow);
+ result = _YKvalidator.ExtractAttestion(result, _policy, dbRow, out var yubikeyInfo);
+ result = _YKvalidator.VerifyRequest(result, policy, yubikeyInfo, dbRow.RequestID);
+ Assert.False(result.DeniedForIssuance);
+ PrintResult(result);
+
+ output.WriteLine(policy.SaveToString());
+ }
+ [Fact]
+ public void Validate_Slot_with_0x_10021()
+ {
+ CertificateDatabaseRow dbRow = new CertificateDatabaseRow(_yubikey_valid_5_4_3_Once_Never_UsbAKeychain_9a_Normal_RSA_2048_CSR, CertCli.CR_IN_PKCS10, null, 10020);
+ CertificateRequestPolicy policy = _policy;
+ var result = new CertificateRequestValidationResult(dbRow);
+
+ // Required slot 0x9a, which needs to match 9a
+ policy = _policy;
+ policy.YubikeyPolicy[0].Slot = new List { "0x9a" };
+ result = new CertificateRequestValidationResult(dbRow);
+ result = _YKvalidator.ExtractAttestion(result, _policy, dbRow, out var yubikeyInfo);
+ result = _YKvalidator.VerifyRequest(result, policy, yubikeyInfo, dbRow.RequestID);
+ Assert.False(result.DeniedForIssuance);
+ PrintResult(result);
+
+ output.WriteLine(policy.SaveToString());
+ }
+ [Fact]
+ public void Validate_Slot_incorrect_with_0x_10022()
+ {
+ CertificateDatabaseRow dbRow = new CertificateDatabaseRow(_yubikey_valid_5_4_3_Once_Never_UsbAKeychain_9a_Normal_RSA_2048_CSR, CertCli.CR_IN_PKCS10, null, 10020);
+ CertificateRequestPolicy policy = _policy;
+ var result = new CertificateRequestValidationResult(dbRow);
+
+ // Should not match the csr which is 9A
+ policy = _policy;
+ policy.YubikeyPolicy[0].Slot = new List { "0x9e" };
+ result = new CertificateRequestValidationResult(dbRow);
+ result = _YKvalidator.ExtractAttestion(result, _policy, dbRow, out var yubikeyInfo);
+ result = _YKvalidator.VerifyRequest(result, policy, yubikeyInfo, dbRow.RequestID);
+ Assert.True(result.DeniedForIssuance);
+ PrintResult(result);
+
+ output.WriteLine(policy.SaveToString());
}
}
}
\ No newline at end of file
diff --git a/TameMyCerts/EWTLogger.cs b/TameMyCerts/EWTLogger.cs
index 8d4a20d..9a048fe 100644
--- a/TameMyCerts/EWTLogger.cs
+++ b/TameMyCerts/EWTLogger.cs
@@ -107,6 +107,14 @@ public void TMC_93_Policy_Unknown_XML_Attribute(string attributeName, string att
WriteEvent(93, attributeName, attributeValue, lineNumber, linePosition);
}
}
+ [Event(94, Level = EventLevel.Critical, Channel = EventChannel.Admin, Task = Tasks.XMLParser, Keywords = EventKeywords.None)]
+ public void TMC_94_XML_Parsing_error(string filename, string error)
+ {
+ if (IsEnabled())
+ {
+ WriteEvent(94, filename, error);
+ }
+ }
#endregion
diff --git a/TameMyCerts/LocalizedStrings.Designer.cs b/TameMyCerts/LocalizedStrings.Designer.cs
index 39bc951..9c0da97 100644
--- a/TameMyCerts/LocalizedStrings.Designer.cs
+++ b/TameMyCerts/LocalizedStrings.Designer.cs
@@ -420,7 +420,7 @@ internal static string event_TMC_91_Policy_Read {
}
///
- /// Looks up a localized string similar to XML policy unknown Element: {0} at line {1}, position {2}.
+ /// Looks up a localized string similar to XML policy unknown Element: {0} at line {1}, position {2}, Elements are case sensitive..
///
internal static string event_TMC_92_Policy_Unknown_XML_Element {
get {
@@ -437,6 +437,15 @@ internal static string event_TMC_93_Policy_Unknown_XML_Attribute {
}
}
+ ///
+ /// Looks up a localized string similar to Unable to parse '{0}', {1}..
+ ///
+ internal static string event_TMC_94_XML_Parsing_error {
+ get {
+ return ResourceManager.GetString("event_TMC_94_XML_Parsing_error", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to The request {1} was rejected due to policy: \r\n{0}.
///
diff --git a/TameMyCerts/LocalizedStrings.resx b/TameMyCerts/LocalizedStrings.resx
index 330ef8a..82b9c6c 100644
--- a/TameMyCerts/LocalizedStrings.resx
+++ b/TameMyCerts/LocalizedStrings.resx
@@ -436,7 +436,7 @@ with the content:
{1}
- XML policy unknown Element: {0} at line {1}, position {2}
+ XML policy unknown Element: {0} at line {1}, position {2}, Elements are case sensitive.
Unknown Attribute: {0}='{1}' at line {2}, position {3}
@@ -459,4 +459,7 @@ with the content:
Unable to determine the msds-TokenGroupNames attribute for {0}. Note that this attribute is only available on Windows Server 2016 and newer Domain Controllers.
+
+ Unable to parse '{0}', {1}.
+
\ No newline at end of file
diff --git a/TameMyCerts/Models/CertificateRequestPolicy.cs b/TameMyCerts/Models/CertificateRequestPolicy.cs
index b7e35a4..a614474 100644
--- a/TameMyCerts/Models/CertificateRequestPolicy.cs
+++ b/TameMyCerts/Models/CertificateRequestPolicy.cs
@@ -84,8 +84,9 @@ public class CertificateRequestPolicy
[XmlElement(ElementName = "DirectoryServicesMapping")]
public DirectoryServicesMapping DirectoryServicesMapping { get; set; }
- [XmlElement(ElementName = "YubikeyPolicy")]
- public List YubikeyPolicy { get; set; }
+ [XmlArray(ElementName = "YubiKeyPolicies")]
+ [XmlArrayItem(ElementName = "YubiKeyPolicy")]
+ public List YubikeyPolicy { get; set; } = new();
[XmlElement(ElementName = "SupplementDnsNames")]
public bool SupplementDnsNames { get; set; }
@@ -164,7 +165,7 @@ public string SaveToString()
}
private static void UnknownElementHandler(object sender, XmlElementEventArgs e)
{
- ETWLogger.Log.TMC_92_Policy_Unknown_XML_Element(e.Element.Name, e.LineNumber, e.LinePosition);
+ ETWLogger.Log.TMC_92_Policy_Unknown_XML_Element(e.Element.Name, e.LineNumber, e.LinePosition);
}
// Event handler for unknown attributes
diff --git a/TameMyCerts/Models/CertificateRequestPolicyCacheEntry.cs b/TameMyCerts/Models/CertificateRequestPolicyCacheEntry.cs
index 8810a84..55afe89 100644
--- a/TameMyCerts/Models/CertificateRequestPolicyCacheEntry.cs
+++ b/TameMyCerts/Models/CertificateRequestPolicyCacheEntry.cs
@@ -30,6 +30,7 @@ public CertificateRequestPolicyCacheEntry(string fileName)
ErrorMessage = ex.InnerException != null
? $"{ex.Message} {ex.InnerException.Message}"
: ex.Message;
+ ETWLogger.Log.TMC_94_XML_Parsing_error(fileName, ErrorMessage);
}
LastUpdate = DateTimeOffset.Now;
diff --git a/TameMyCerts/Models/YubikeyPolicy.cs b/TameMyCerts/Models/YubikeyPolicy.cs
index cc630ac..c6da6db 100644
--- a/TameMyCerts/Models/YubikeyPolicy.cs
+++ b/TameMyCerts/Models/YubikeyPolicy.cs
@@ -26,7 +26,7 @@
namespace TameMyCerts.Models
{
// Must be public due to XML serialization, otherwise 0x80131509 / System.InvalidOperationException
- [XmlRoot(ElementName = "YubikeyPolicy")]
+ [XmlRoot(ElementName = "YubiKeyPolicy")]
public class YubikeyPolicy
{
[XmlElement(ElementName = "Action")]
@@ -50,6 +50,9 @@ public class YubikeyPolicy
[XmlArray(ElementName = "Edition")]
[XmlArrayItem(ElementName = "string")]
public List Edition { get; set; } = new List();
+ [XmlArray(ElementName = "Slot")]
+ [XmlArrayItem(ElementName = "string")]
+ public List Slot { get; set; } = new List();
[XmlArray(ElementName = "KeyAlgorithm")]
[XmlArrayItem(ElementName = "string")]
public List KeyAlgorithmFamilies { get; set; } = new List();
diff --git a/TameMyCerts/Validators/YubikeyValidator.cs b/TameMyCerts/Validators/YubikeyValidator.cs
index fb864bb..025acda 100644
--- a/TameMyCerts/Validators/YubikeyValidator.cs
+++ b/TameMyCerts/Validators/YubikeyValidator.cs
@@ -185,6 +185,15 @@ private bool ObjectMatchesPolicy(YubikeyPolicy policy, YubikeyObject yubikey)
}
#endregion
+ #region Slot
+ // Look if the slot is in the policy, if not, say that we arent matching
+ // Look for both 0xXX and XX
+ if (policy.Slot.Any() && !(policy.Slot.Any(s => s.Equals(yubikey.Slot, StringComparison.OrdinalIgnoreCase)) || policy.Slot.Any(s => s.Equals($"0x{yubikey.Slot}", StringComparison.OrdinalIgnoreCase))))
+ {
+ return false;
+ }
+ #endregion
+
if (policy.KeyAlgorithmFamilies.Any() && ! policy.KeyAlgorithmFamilies.Contains(yubikey.keyAlgorithm))
{
return false;
diff --git a/TameMyCerts/install.ps1 b/TameMyCerts/install.ps1
index a472b89..2c21c20 100644
--- a/TameMyCerts/install.ps1
+++ b/TameMyCerts/install.ps1
@@ -257,11 +257,15 @@ if (-not $Uninstall.IsPresent) {
New-Item -Path $AppInstallDirectory -ItemType Directory -Force -ErrorAction SilentlyContinue | Out-Null
New-Item -Path "$AppInstallDirectory\\runtimes\win\lib\net8.0" -ItemType Directory -Force -ErrorAction SilentlyContinue | Out-Null
- $FileList | ForEach-Object -Process {
+ # Only copy the files if .\install.ps1 is run from another folder than the AppInstallDirectory
+ if ($BaseDirectory -ne $AppInstallDirectory)
+ {
+ $FileList | ForEach-Object -Process {
- Write-Verbose -Message "Copying $_ to $AppInstallDirectory."
+ Write-Verbose -Message "Copying $_ to $AppInstallDirectory."
- Copy-Item -Path "$BaseDirectory\$_" -Destination "$AppInstallDirectory\$_" -Force
+ Copy-Item -Path "$BaseDirectory\$_" -Destination "$AppInstallDirectory\$_" -Force
+ }
}
Write-Verbose -Message "Registering $PolicyModuleName policy module COM Object"
diff --git a/examples/Sample_Online_Yubikey_Verification_and_Rewrite.xml b/examples/Sample_Online_Yubikey_Verification_and_Rewrite.xml
index 0e1e00f..7fb453a 100644
--- a/examples/Sample_Online_Yubikey_Verification_and_Rewrite.xml
+++ b/examples/Sample_Online_Yubikey_Verification_and_Rewrite.xml
@@ -9,18 +9,20 @@
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-
- 5.6.9
- ECC
- Deny
-
-
-
- Always
- Once
-
- Allow
-
+
+
+ 5.6.9
+ ECC
+ Deny
+
+
+
+ Always
+ Once
+
+ Allow
+
+
commonName