-
Notifications
You must be signed in to change notification settings - Fork 8.4k
Bring back Certificate provider parameters #10622
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -31,20 +31,18 @@ namespace Microsoft.PowerShell.Commands | |||||
| { | ||||||
|
iSazonov marked this conversation as resolved.
Outdated
iSazonov marked this conversation as resolved.
Outdated
iSazonov marked this conversation as resolved.
Outdated
iSazonov marked this conversation as resolved.
Outdated
iSazonov marked this conversation as resolved.
|
||||||
| /// <summary> | ||||||
| /// Defines the Certificate Provider dynamic parameters. | ||||||
| /// | ||||||
| /// We only support one dynamic parameter for Win 7 and earlier: | ||||||
| /// CodeSigningCert | ||||||
| /// If provided, we only return certificates valid for signing code or | ||||||
| /// scripts. | ||||||
| /// </summary> | ||||||
|
|
||||||
| internal sealed class CertificateProviderCodeSigningDynamicParameters | ||||||
| internal sealed class CertificateProviderDynamicParameters | ||||||
| { | ||||||
| /// <summary> | ||||||
| /// Switch that controls whether we only return | ||||||
| /// Gets or sets a switch that controls whether we only return | ||||||
| /// code signing certs. | ||||||
| /// </summary> | ||||||
| [Parameter()] | ||||||
| [Parameter] | ||||||
| public SwitchParameter CodeSigningCert | ||||||
| { | ||||||
| get { return _codeSigningCert; } | ||||||
|
|
@@ -53,6 +51,70 @@ public SwitchParameter CodeSigningCert | |||||
| } | ||||||
|
|
||||||
| private SwitchParameter _codeSigningCert = new SwitchParameter(); | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// Gets or sets a filter that controls whether we only return | ||||||
| /// data encipherment certs. | ||||||
| /// </summary> | ||||||
| [Parameter] | ||||||
| public SwitchParameter DocumentEncryptionCert | ||||||
| { | ||||||
| get; | ||||||
| set; | ||||||
| } | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// Gets or sets a filter that controls whether we only return | ||||||
| /// server authentication certs. | ||||||
| /// </summary> | ||||||
| [Parameter] | ||||||
| public SwitchParameter SSLServerAuthentication | ||||||
| { | ||||||
| get; | ||||||
| set; | ||||||
| } | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// Gets or sets a filter by DNSName. | ||||||
| /// Expected content is a single DNS Name that may start and/or end | ||||||
| /// with '*': "contoso.com" or "*toso.c*". | ||||||
| /// All WildcardPattern class features supported. | ||||||
| /// </summary> | ||||||
| [Parameter] | ||||||
| public string DnsName | ||||||
| { | ||||||
| get; | ||||||
| set; | ||||||
| } | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// Gets or sets a filter by EKU. | ||||||
| /// Expected content is one or more OID strings: | ||||||
| /// "1.3.6.1.5.5.7.3.1", "*Server*", etc. | ||||||
| /// For a cert to match, it must be valid for all listed OIDs. | ||||||
| /// All WildcardPattern class features supported. | ||||||
| /// </summary> | ||||||
| [Parameter] | ||||||
| public string[] Eku | ||||||
| { | ||||||
| get; | ||||||
| set; | ||||||
| } | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// Gets or sets a filter by the number of valid days. | ||||||
| /// Expected content is a non-negative integer. | ||||||
| /// "0" matches all certs that have already expired. | ||||||
| /// "1" matches all certs that are currently valid and will expire | ||||||
| /// by next day (local time). | ||||||
| /// </summary> | ||||||
| [Parameter] | ||||||
| [ValidateRange(ValidateRangeKind.NonNegative)] | ||||||
| public int ExpiringInDays | ||||||
| { | ||||||
| get; | ||||||
| set; | ||||||
| } = -1; | ||||||
| } | ||||||
|
|
||||||
| /// <summary> | ||||||
|
|
@@ -169,7 +231,7 @@ internal sealed class ProviderRemoveItemDynamicParameters | |||||
| /// Switch that controls whether we should delete private key | ||||||
| /// when remove a certificate. | ||||||
| /// </summary> | ||||||
| [Parameter()] | ||||||
| [Parameter] | ||||||
| public SwitchParameter DeleteKey | ||||||
| { | ||||||
| get | ||||||
|
|
@@ -1185,15 +1247,11 @@ protected override void GetItem(string path) | |||||
| } | ||||||
| else | ||||||
| { | ||||||
| // The filter is non null. If the certificate | ||||||
| // satisfies the filter, output it. Otherwise, don't. | ||||||
|
|
||||||
| // The filter is non null. If the certificate | ||||||
| // satisfies the filter, output it. Otherwise, don't. | ||||||
| X509Certificate2 cert = item as X509Certificate2; | ||||||
| Dbg.Diagnostics.Assert(cert != null, "item should be a certificate"); | ||||||
|
|
||||||
| // If it's Win8 or above, filter matching for certain properties is done by | ||||||
| // the certificate enumeration filter at the API level. In that case, | ||||||
| // filter.Purpose will be 'None' and MatchesFilter will return 'True'. | ||||||
| if (MatchesFilter(cert, filter)) | ||||||
| { | ||||||
| WriteItemObject(item, path, isContainer); | ||||||
|
|
@@ -2212,7 +2270,7 @@ protected override bool IsItemContainer(string path) | |||||
| /// </returns> | ||||||
| protected override object GetItemDynamicParameters(string path) | ||||||
| { | ||||||
| return new CertificateProviderCodeSigningDynamicParameters(); | ||||||
| return new CertificateProviderDynamicParameters(); | ||||||
| } | ||||||
|
|
||||||
| /// <summary> | ||||||
|
|
@@ -2234,7 +2292,7 @@ protected override object GetItemDynamicParameters(string path) | |||||
| /// </returns> | ||||||
| protected override object GetChildItemsDynamicParameters(string path, bool recurse) | ||||||
| { | ||||||
| return new CertificateProviderCodeSigningDynamicParameters(); | ||||||
| return new CertificateProviderDynamicParameters(); | ||||||
| } | ||||||
|
|
||||||
| #endregion DriveCmdletProvider overrides | ||||||
|
|
@@ -2607,15 +2665,49 @@ private CertificateFilterInfo GetFilter() | |||||
|
|
||||||
| if (DynamicParameters != null) | ||||||
| { | ||||||
| CertificateProviderCodeSigningDynamicParameters dp = | ||||||
| DynamicParameters as CertificateProviderCodeSigningDynamicParameters; | ||||||
| CertificateProviderDynamicParameters dp = | ||||||
| DynamicParameters as CertificateProviderDynamicParameters; | ||||||
| if (dp != null) | ||||||
| { | ||||||
| if (dp.CodeSigningCert) | ||||||
| { | ||||||
| filter = new CertificateFilterInfo(); | ||||||
| filter.Purpose = CertificatePurpose.CodeSigning; | ||||||
| } | ||||||
|
|
||||||
| if (dp.DocumentEncryptionCert) | ||||||
| { | ||||||
| filter = filter ?? new CertificateFilterInfo(); | ||||||
| filter.Purpose = CertificatePurpose.DocumentEncryption; | ||||||
| } | ||||||
|
|
||||||
| if (dp.DnsName != null) | ||||||
| { | ||||||
| filter = filter ?? new CertificateFilterInfo(); | ||||||
| filter.DnsName = new WildcardPattern(dp.DnsName, WildcardOptions.IgnoreCase); | ||||||
| } | ||||||
|
|
||||||
| if (dp.Eku != null) | ||||||
| { | ||||||
| filter = filter ?? new CertificateFilterInfo(); | ||||||
| filter.Eku = new List<WildcardPattern>(); | ||||||
| foreach (var pattern in dp.Eku) | ||||||
| { | ||||||
| filter.Eku.Add(new WildcardPattern(pattern, WildcardOptions.IgnoreCase)); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| if (dp.ExpiringInDays >= 0) | ||||||
| { | ||||||
| filter = filter ?? new CertificateFilterInfo(); | ||||||
| filter.Expiring = DateTime.Now.AddDays(dp.ExpiringInDays); | ||||||
| } | ||||||
|
|
||||||
| if (dp.SSLServerAuthentication) | ||||||
| { | ||||||
| filter = filter ?? new CertificateFilterInfo(); | ||||||
| filter.SSLServerAuthentication = true; | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -2634,42 +2726,131 @@ private bool IncludeArchivedCerts() | |||||
| return includeArchivedCerts; | ||||||
| } | ||||||
|
|
||||||
| // If it's Win8 or above, filter matching for certain properties is done by | ||||||
| // the certificate enumeration filter at the API level. In that case, | ||||||
| // filter.Purpose will be 'None' and MatchesFilter will return 'True'. | ||||||
| private static bool MatchesFilter(X509Certificate2 cert, | ||||||
| CertificateFilterInfo filter) | ||||||
| private static bool MatchesFilter(X509Certificate2 cert, CertificateFilterInfo filter) | ||||||
| { | ||||||
| // | ||||||
| // no filter means, match everything | ||||||
| // | ||||||
| if ((filter == null) || | ||||||
| (filter.Purpose == CertificatePurpose.NotSpecified) || | ||||||
| (filter.Purpose == CertificatePurpose.All)) | ||||||
| // No filter means, match everything | ||||||
| if (filter == null) | ||||||
| { | ||||||
| return true; | ||||||
| } | ||||||
|
|
||||||
| if (filter.Expiring > DateTime.MinValue && !SecuritySupport.CertExpiresByTime(cert, filter.Expiring)) | ||||||
| { | ||||||
| return false; | ||||||
| } | ||||||
|
|
||||||
| if (filter.DnsName != null && !CertContainsName(cert, filter.DnsName)) | ||||||
| { | ||||||
| return false; | ||||||
| } | ||||||
|
|
||||||
| if (filter.Eku != null && !CertContainsEku(cert, filter.Eku)) | ||||||
| { | ||||||
| return false; | ||||||
| } | ||||||
|
|
||||||
| if (filter.SSLServerAuthentication && !CertIsSSLServerAuthentication(cert)) | ||||||
| { | ||||||
| return false; | ||||||
| } | ||||||
|
|
||||||
| switch (filter.Purpose) | ||||||
| { | ||||||
| case CertificatePurpose.CodeSigning: | ||||||
| if (SecuritySupport.CertIsGoodForSigning(cert)) | ||||||
| { | ||||||
| return true; | ||||||
| } | ||||||
| return SecuritySupport.CertIsGoodForSigning(cert); | ||||||
|
|
||||||
| case CertificatePurpose.DocumentEncryption: | ||||||
| return SecuritySupport.CertIsGoodForEncryption(cert); | ||||||
|
|
||||||
| case CertificatePurpose.NotSpecified: | ||||||
| case CertificatePurpose.All: | ||||||
| return true; | ||||||
|
|
||||||
| default: | ||||||
| break; | ||||||
| } | ||||||
|
|
||||||
| case CertificatePurpose.DocumentEncryption: | ||||||
| if (SecuritySupport.CertIsGoodForEncryption(cert)) | ||||||
| return false; | ||||||
| } | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// Check if the specified certificate has the name in DNS name list. | ||||||
| /// </summary> | ||||||
| /// <param name="cert">Certificate object.</param> | ||||||
| /// <param name="pattern">Wildcard pattern for DNS name to search.</param> | ||||||
| /// <returns>True on success, false otherwise.</returns> | ||||||
| internal static bool CertContainsName(X509Certificate2 cert, WildcardPattern pattern) | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have you verified this will work with punycode?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you mean that Windows PowerShell support also punycode in dnsname filter? I did not find this in docs https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.security/about/about_certificate_provider?view=powershell-6 What is a scenario where it could be used?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When the name in the cert is in punycode. This is a compliance requirement. you deleted the code to do this.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Clear about compliance. Not clear what code do you mean.
It is not clear how punycode converted to Unicode. It seems this did unpublic code (in P/Invoke) which we removed long ago.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I looked original code which was removed and see only one difference - DNSName parameter was DnsNameRepresentation type, now string. |
||||||
| { | ||||||
| List<DnsNameRepresentation> list = (new DnsNameProperty(cert)).DnsNameList; | ||||||
| foreach (DnsNameRepresentation dnsName in list) | ||||||
| { | ||||||
| if (pattern.IsMatch(dnsName.Unicode)) | ||||||
|
TravisEz13 marked this conversation as resolved.
Outdated
|
||||||
| { | ||||||
| return true; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| return false; | ||||||
| } | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// Check if the specified certificate is a server authentication certificate. | ||||||
| /// </summary> | ||||||
| /// <param name="cert">Certificate object.</param> | ||||||
| /// <returns>True on success, false otherwise.</returns> | ||||||
| internal static bool CertIsSSLServerAuthentication(X509Certificate2 cert) | ||||||
| { | ||||||
| X509ExtensionCollection extentionList = cert.Extensions; | ||||||
| foreach (var extension in extentionList) | ||||||
| { | ||||||
| if (extension is X509EnhancedKeyUsageExtension eku) | ||||||
| { | ||||||
| foreach (Oid usage in eku.EnhancedKeyUsages) | ||||||
| { | ||||||
| return true; | ||||||
| if (usage.Value.Equals(CertificateFilterInfo.OID_PKIX_KP_SERVER_AUTH, StringComparison.Ordinal)) | ||||||
| { | ||||||
| return true; | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| break; | ||||||
| return false; | ||||||
| } | ||||||
|
|
||||||
| default: | ||||||
| break; | ||||||
| /// <summary> | ||||||
| /// Check if the specified certificate contains EKU matching all of these patterns. | ||||||
| /// </summary> | ||||||
| /// <param name="cert">Certificate object.</param> | ||||||
| /// <param name="ekuPatterns">EKU patterns.</param> | ||||||
| /// <returns>True on success, false otherwise.</returns> | ||||||
| internal static bool CertContainsEku(X509Certificate2 cert, List<WildcardPattern> ekuPatterns) | ||||||
| { | ||||||
| X509ExtensionCollection extensionList = cert.Extensions; | ||||||
| foreach (var extension in extensionList) | ||||||
| { | ||||||
| if (extension is X509EnhancedKeyUsageExtension eku) | ||||||
| { | ||||||
| OidCollection enhancedKeyUsages = eku.EnhancedKeyUsages; | ||||||
| foreach (WildcardPattern ekuPattern in ekuPatterns) | ||||||
| { | ||||||
| bool patternPassed = false; | ||||||
| foreach (var usage in enhancedKeyUsages) | ||||||
| { | ||||||
| if (ekuPattern.IsMatch(usage.Value) || ekuPattern.IsMatch(usage.FriendlyName)) | ||||||
| { | ||||||
| return true; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| if (!patternPassed) | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can remove this |
||||||
| { | ||||||
| return false; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| return true; | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| } | ||||||
| } | ||||||
|
|
||||||
| return false; | ||||||
|
|
@@ -3195,7 +3376,7 @@ public List<DnsNameRepresentation> DnsNameList | |||||
| } | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// Constructor for EkuList. | ||||||
| /// Constructor for DnsNameProperty. | ||||||
| /// </summary> | ||||||
| public DnsNameProperty(X509Certificate2 cert) | ||||||
| { | ||||||
|
|
||||||
Uh oh!
There was an error while loading. Please reload this page.