From 8dc637e3b918d764f880abe7bf816bf6f005380e Mon Sep 17 00:00:00 2001 From: Shaopeng <81775155+shaopeng-gh@users.noreply.github.com> Date: Fri, 8 Dec 2023 13:12:12 -0800 Subject: [PATCH] BUG: Fix missing error code to error description mappings for `CryptoError` (#969) * Update BuildCryptoErrorDescriptions() * Update error message * BUG: Fix `ERR998.ExceptionInAnalyze`: `InvalidOperationException: Unrecognized crypto HRESULT: 0x80096011` for check `BA2022.SignSecurely` when the signature is malformed, by adding missing error code to error description mappings. * Update texts * add InternalsVisibleToAttribute --------- Co-authored-by: Michael C. Fanning --- ReleaseHistory.md | 2 +- src/BinSkim.Rules/CryptoErrors.cs | 2 + src/BinSkim.Rules/RulesExtensionMethods.cs | 73 ++++++++++++++++++- .../CryptoErrorEnumTests.cs | 25 +++++++ 4 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 src/Test.UnitTests.BinSkim.Rules/CryptoErrorEnumTests.cs diff --git a/ReleaseHistory.md b/ReleaseHistory.md index 6a659c1f..d6f58d26 100644 --- a/ReleaseHistory.md +++ b/ReleaseHistory.md @@ -14,9 +14,9 @@ - UEE => eliminate unhandled exceptions in engine - DEP => upgrade dependency versions - NEW => new feature - ## UNRELEASED * DEP: Update `Sarif.Sdk` submodule from [bc8cb57 to fd6e615](https://github.com/microsoft/sarif-sdk/compare/bc8cb57...fd6e615). Reference [SARIF SDK Release History](https://github.com/microsoft/sarif-sdk/blob/fd6e615/ReleaseHistory.md). +* BUG: Fix `ERR998.ExceptionInAnalyze`: `InvalidOperationException: Unrecognized crypto HRESULT: 0x80096011` for check `BA2022.SignSecurely` when the signature is malformed, by adding missing error code to error description mappings. [969](https://github.com/microsoft/binskim/pull/969) ## **v4.2.1** * FPS: `BA2004.EnableSecureSourceCodeHashing` now will no longer generate false positives on precompiled headers, they are always without hash. [#965](https://github.com/microsoft/binskim/pull/965) diff --git a/src/BinSkim.Rules/CryptoErrors.cs b/src/BinSkim.Rules/CryptoErrors.cs index 639c8a75..484735a8 100644 --- a/src/BinSkim.Rules/CryptoErrors.cs +++ b/src/BinSkim.Rules/CryptoErrors.cs @@ -10,6 +10,8 @@ namespace Microsoft.CodeAnalysis.IL.Rules /// public enum CryptoError : uint { + // Whenever this file is modified, + // RulesExtensionMethods.BuildCryptoErrorDescriptions() must be updated accordingly. ERROR_SUCCESS = 0, CERT_E_CHAINING = 0x800B010A, // A certificate chain could not be built to a trusted root authority. CERT_E_CN_NO_MATCH = 0x800B010F, // The certificate's CN name does not match the passed value. diff --git a/src/BinSkim.Rules/RulesExtensionMethods.cs b/src/BinSkim.Rules/RulesExtensionMethods.cs index 519fc555..1111c39b 100644 --- a/src/BinSkim.Rules/RulesExtensionMethods.cs +++ b/src/BinSkim.Rules/RulesExtensionMethods.cs @@ -4,10 +4,12 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using Microsoft.CodeAnalysis.BinaryParsers.ProgramDatabase; +[assembly: InternalsVisibleToAttribute("Test.UnitTests.BinSkim.Rules")] namespace Microsoft.CodeAnalysis.IL.Rules { public static class RulesExtensionMethods @@ -130,10 +132,11 @@ private static string CreateLibraryKey(ObjectModuleDetails objectModuleDetail) private static readonly Dictionary s_cryptoErrorToDescriptionMap = BuildCryptoErrorDescriptions(); - private static Dictionary BuildCryptoErrorDescriptions() + internal static Dictionary BuildCryptoErrorDescriptions() { var result = new Dictionary() { + // This Dictionary should contain all cases from enum CryptoError. { CryptoError.ERROR_SUCCESS, "Call succeeded." }, { CryptoError.CERT_E_CHAINING, "An internal certificate chaining error has occurred." }, { CryptoError.CERT_E_CN_NO_MATCH, "The certificate's CN name does not match the passed value." }, @@ -153,15 +156,64 @@ private static Dictionary BuildCryptoErrorDescriptions() { CryptoError.CERT_E_UNTRUSTEDTESTROOT, "The certification path terminates with the test root which is not trusted with the current policy settings." }, { CryptoError.CERT_E_VALIDITYPERIODNESTING, "The validity periods of the certification chain do not nest correctly." }, { CryptoError.CERT_E_WRONG_USAGE, "The certificate is not valid for the requested usage." }, + { CryptoError.CERTSRV_E_ADMIN_DENIED_REQUEST, "The request was denied by a certificate manager or CA administrator." }, + { CryptoError.CERTSRV_E_ALIGNMENT_FAULT, "A memory reference caused a data alignment fault." }, + { CryptoError.CERTSRV_E_ARCHIVED_KEY_REQUIRED, "The request is missing a required private key for archival by the server." }, + { CryptoError.CERTSRV_E_ARCHIVED_KEY_UNEXPECTED, "The request includes a private key for archiving by the server, but key archiving is not enabled for the specified certificate template." }, + { CryptoError.CERTSRV_E_BAD_RENEWAL_CERT_ATTRIBUTE, "The request contains an invalid renewal certificate attribute." }, + { CryptoError.CERTSRV_E_BAD_RENEWAL_SUBJECT, "The request was made on behalf of a subject other than the caller. The certificate template must be configured to require at least one signature to authorize the request." }, + { CryptoError.CERTSRV_E_BAD_REQUEST_KEY_ARCHIVAL, "The request is incorrectly formatted. The encrypted private key must be in an unauthenticated attribute in an outermost signature." }, { CryptoError.CERTSRV_E_BAD_REQUESTSTATUS, "The request's current status does not allow this operation." }, { CryptoError.CERTSRV_E_BAD_REQUESTSUBJECT, "The request subject name is invalid or too long." }, + { CryptoError.CERTSRV_E_BAD_TEMPLATE_VERSION, "The request template version is newer than the supported template version." }, + { CryptoError.CERTSRV_E_CERT_TYPE_OVERLAP, "The certificate template renewal period is longer than the certificate validity period. The template should be reconfigured or the CA certificate renewed." }, + { CryptoError.CERTSRV_E_CORRUPT_KEY_ATTESTATION, "The request public key is not consistent with the private key attestation data." }, + { CryptoError.CERTSRV_E_DOWNLEVEL_DC_SSL_OR_UPGRADE, "The contacted domain controller cannot support signed LDAP traffic. Update the domain controller or configure Certificate Services to use SSL for Active Directory access." }, { CryptoError.CERTSRV_E_ENCODING_LENGTH, "The certificate contains an encoded length that is potentially incompatible with older enrollment software." }, + { CryptoError.CERTSRV_E_ENCRYPTION_CERT_REQUIRED, "No encryption certificate was specified." }, + { CryptoError.CERTSRV_E_ENROLL_DENIED, "The permissions on this certification authority do not allow the current user to enroll for certificates." }, + { CryptoError.CERTSRV_E_EXPIRED_CHALLENGE, "The private key attestation challenge cannot be validated because the encryption certificate has expired, or the certificate or key is unavailable." }, + { CryptoError.CERTSRV_E_INVALID_ATTESTATION, "The certification authority cannot validate the private key attestation data." }, { CryptoError.CERTSRV_E_INVALID_CA_CERTIFICATE, "The certification authority's certificate contains invalid data." }, + { CryptoError.CERTSRV_E_INVALID_EK, "The certification authority cannot interpret or verify the endorsement key information supplied in the request, or the information is inconsistent." }, + { CryptoError.CERTSRV_E_INVALID_IDBINDING, "The certification authority cannot validate the Attestation Identity Key Id Binding." }, + { CryptoError.CERTSRV_E_INVALID_REQUESTID, "A valid Request ID was not detected in the request attributes, or an invalid one was submitted." }, + { CryptoError.CERTSRV_E_INVALID_RESPONSE, "The client's response could not be validated. It is either unexpected or incorrect." }, + { CryptoError.CERTSRV_E_ISSUANCE_POLICY_REQUIRED, "The request is missing one or more required signature issuance policies." }, + { CryptoError.CERTSRV_E_KEY_ARCHIVAL_NOT_CONFIGURED, "Cannot archive private key. The certification authority is not configured for key archival." }, + { CryptoError.CERTSRV_E_KEY_ATTESTATION, "The request does not support private key attestation as defined in the certificate template." }, + { CryptoError.CERTSRV_E_KEY_ATTESTATION_NOT_SUPPORTED, "Failed to create an attested key. This computer or the cryptographic provider may not meet the hardware requirements to support key attestation." }, + { CryptoError.CERTSRV_E_KEY_LENGTH, "The public key does not meet the minimum size required by the specified certificate template." }, + { CryptoError.CERTSRV_E_NO_CAADMIN_DEFINED, "At least one security principal must have the permission to manage this CA." }, { CryptoError.CERTSRV_E_NO_CERT_TYPE, "The request contains no certificate template information." }, + { CryptoError.CERTSRV_E_NO_DB_SESSIONS, "An attempt was made to open a Certification Authority database session, but there are already too many active sessions. The server may need to be configured to allow additional sessions." }, + { CryptoError.CERTSRV_E_NO_POLICY_SERVER, "An enrollment policy server cannot be located." }, { CryptoError.CERTSRV_E_NO_REQUEST, "The request does not exist." }, + { CryptoError.CERTSRV_E_NO_VALID_KRA, "Cannot archive private key. The certification authority could not verify one or more key recovery certificates." }, + { CryptoError.CERTSRV_E_PENDING_CLIENT_RESPONSE, "The request is locked against edits until a response is received from the client." }, { CryptoError.CERTSRV_E_PROPERTY_EMPTY, "The requested property value is empty." }, + { CryptoError.CERTSRV_E_RENEWAL_BAD_PUBLIC_KEY, "The certificate template requires renewal with the same public key, but the request uses a different public key." }, + { CryptoError.CERTSRV_E_REQUEST_PRECERTIFICATE_MISMATCH, "The request is not consistent with the previously generated precertificate." }, + { CryptoError.CERTSRV_E_RESTRICTEDOFFICER, "The operation is denied. It can only be performed by a certificate manager that is allowed to manage certificates for the current requester." }, + { CryptoError.CERTSRV_E_ROLECONFLICT, "The operation is denied. The user has multiple roles assigned and the certification authority is configured to enforce role separation." }, + { CryptoError.CERTSRV_E_SEC_EXT_DIRECTORY_SID_REQUIRED, "The Active Directory SID is unavailable and cannot be added to the custom security extension." }, { CryptoError.CERTSRV_E_SERVER_SUSPENDED, "Certificate service has been suspended for a database restore operation." }, + { CryptoError.CERTSRV_E_SIGNATURE_COUNT, "The request is missing one or more required signatures." }, + { CryptoError.CERTSRV_E_SIGNATURE_POLICY_REQUIRED, "The request is missing required signature policy information." }, + { CryptoError.CERTSRV_E_SIGNATURE_REJECTED, "One or more signatures did not include the required application or issuance policies. The request is missing one or more required valid signatures." }, + { CryptoError.CERTSRV_E_SMIME_REQUIRED, "The request is missing a required SMIME capabilities extension." }, + { CryptoError.CERTSRV_E_SUBJECT_ALT_NAME_REQUIRED, "The request is missing a required Subject Alternate name extension." }, + { CryptoError.CERTSRV_E_SUBJECT_DIRECTORY_GUID_REQUIRED, "The Active Directory GUID is unavailable and cannot be added to the Subject Alternate name." }, + { CryptoError.CERTSRV_E_SUBJECT_DNS_REQUIRED, "The DNS name is unavailable and cannot be added to the Subject Alternate name." }, + { CryptoError.CERTSRV_E_SUBJECT_EMAIL_REQUIRED, "The E-Mail name is unavailable and cannot be added to the Subject or Subject Alternate name." }, + { CryptoError.CERTSRV_E_SUBJECT_UPN_REQUIRED, "The UPN is unavailable and cannot be added to the Subject Alternate name." }, + { CryptoError.CERTSRV_E_TEMPLATE_CONFLICT, "The request contains conflicting template information." }, + { CryptoError.CERTSRV_E_TEMPLATE_DENIED, "The permissions on the certificate template do not allow the current user to enroll for this type of certificate." }, + { CryptoError.CERTSRV_E_TEMPLATE_POLICY_REQUIRED, "The template is missing a required signature policy attribute." }, + { CryptoError.CERTSRV_E_TOO_MANY_SIGNATURES, "The certificate template requires too many RA signatures. Only one RA signature is allowed." }, + { CryptoError.CERTSRV_E_UNKNOWN_CERT_TYPE, "One or more certificate templates to be enabled on this certification authority could not be found." }, { CryptoError.CERTSRV_E_UNSUPPORTED_CERT_TYPE, "The requested certificate template is not supported by this CA." }, + { CryptoError.CERTSRV_E_WEAK_SIGNATURE_OR_KEY, "A signature algorithm or public key length does not meet the system's minimum required strength." }, { CryptoError.CRYPT_E_ALREADY_DECRYPTED, "The content of the cryptographic message has already been decrypted." }, { CryptoError.CRYPT_E_ASN1_BADARGS, "ASN1 bad arguments to function call." }, { CryptoError.CRYPT_E_ASN1_BADPDU, "ASN1 function not supported for this PDU." }, @@ -171,6 +223,7 @@ private static Dictionary BuildCryptoErrorDescriptions() { CryptoError.CRYPT_E_ASN1_CONSTRAINT, "ASN1 constraint violated." }, { CryptoError.CRYPT_E_ASN1_CORRUPT, "ASN1 corrupted data." }, { CryptoError.CRYPT_E_ASN1_EOD, "ASN1 unexpected end of data." }, + { CryptoError.CRYPT_E_ASN1_ERROR, "ASN1 Certificate encode/decode error code base. The ASN1 error values are offset by CRYPT_E_ASN1_ERROR." }, { CryptoError.CRYPT_E_ASN1_EXTENDED, "ASN1 skipped unknown extension(s)." }, { CryptoError.CRYPT_E_ASN1_INTERNAL, "ASN1 internal encode or decode error." }, { CryptoError.CRYPT_E_ASN1_LARGE, "ASN1 value too large." }, @@ -216,6 +269,7 @@ private static Dictionary BuildCryptoErrorDescriptions() { CryptoError.CRYPT_E_NOT_FOUND, "Cannot find object or property." }, { CryptoError.CRYPT_E_NOT_IN_CTL, "The subject was not found in a Certificate Trust List (CTL)." }, { CryptoError.CRYPT_E_NOT_IN_REVOCATION_DATABASE, "The certificate is not in the revocation server's database." }, + { CryptoError.CRYPT_E_OBJECT_LOCATOR_OBJECT_NOT_FOUND, "An object could not be located using the object locator infrastructure with the given name." }, { CryptoError.CRYPT_E_OID_FORMAT, "The object identifier is poorly formatted." }, { CryptoError.CRYPT_E_OSS_ERROR, "OSS Certificate encode/decode error code base" }, { CryptoError.CRYPT_E_PENDING_CLOSE, "Final closure is pending until additional frees or closes." }, @@ -258,6 +312,22 @@ private static Dictionary BuildCryptoErrorDescriptions() { CryptoError.MSSIPOTF_E_TABLE_TAGORDER, "Duplicate table tags or tags out of alphabetical order." }, { CryptoError.MSSIPOTF_E_TABLES_OVERLAP, "Two or more tables overlap." }, { CryptoError.NTE_BAD_ALGID, "Invalid algorithm specified." }, + { CryptoError.NTE_BAD_DATA, "Bad Data." }, + { CryptoError.NTE_BAD_FLAGS, "Invalid flags specified." }, + { CryptoError.NTE_BAD_HASH, "Bad Hash." }, + { CryptoError.NTE_BAD_HASH_STATE, "Hash not valid for use in specified state." }, + { CryptoError.NTE_BAD_KEY, "Bad Key." }, + { CryptoError.NTE_BAD_KEY_STATE, "Key not valid for use in specified state." }, + { CryptoError.NTE_BAD_KEYSET, "Keyset does not exist" }, + { CryptoError.NTE_BAD_KEYSET_PARAM, "The Keyset parameter is invalid." }, + { CryptoError.NTE_BAD_LEN, "Bad Length." }, + { CryptoError.NTE_BAD_PROV_TYPE, "Invalid provider type specified." }, + { CryptoError.NTE_BAD_PROVIDER, "Invalid provider specified." }, + { CryptoError.NTE_BAD_PUBLIC_KEY, "Provider's public key is invalid." }, + { CryptoError.NTE_BAD_SIGNATURE, "Invalid Signature." }, + { CryptoError.NTE_BAD_TYPE, "Invalid type specified." }, + { CryptoError.NTE_BAD_UID, "Bad UID." }, + { CryptoError.NTE_BAD_VER, "Bad Version of provider." }, { CryptoError.PERSIST_E_NOTSELFSIZING, "This object does not read and write self-sizing data." }, { CryptoError.PERSIST_E_SIZEDEFINITE, "The size of the data could not be determined." }, { CryptoError.PERSIST_E_SIZEINDEFINITE, "The size of the indefinite-sized data could not be determined." }, @@ -269,6 +339,7 @@ private static Dictionary BuildCryptoErrorDescriptions() { CryptoError.TRUST_E_EXPLICIT_DISTRUST, "The certificate was explicitly marked as untrusted by the user." }, { CryptoError.TRUST_E_FAIL, "Generic trust failure." }, { CryptoError.TRUST_E_FINANCIAL_CRITERIA, "The certificate does not meet or contain the Authenticode financial extensions." }, + { CryptoError.TRUST_E_MALFORMED_SIGNATURE, "The digital signature of the object is malformed. For technical detail, see security bulletin MS13-098." }, { CryptoError.TRUST_E_NO_SIGNER_CERT, "The certificate for the signer of the message is invalid or not found." }, { CryptoError.TRUST_E_NOSIGNATURE, "No signature was present in the subject." }, { CryptoError.TRUST_E_PROVIDER_UNKNOWN, "Unknown trust provider." }, diff --git a/src/Test.UnitTests.BinSkim.Rules/CryptoErrorEnumTests.cs b/src/Test.UnitTests.BinSkim.Rules/CryptoErrorEnumTests.cs new file mode 100644 index 00000000..17491f02 --- /dev/null +++ b/src/Test.UnitTests.BinSkim.Rules/CryptoErrorEnumTests.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; + +using FluentAssertions; + +using Xunit; + +namespace Microsoft.CodeAnalysis.IL.Rules +{ + public class CryptoErrorEnumTests + { + [Fact] + public void CryptoErrorEnumTest_NoMissingValues() + { + IEnumerable enumValues = Enum.GetValues(typeof(CryptoError)).Cast(); + Dictionary dictionaryValues = RulesExtensionMethods.BuildCryptoErrorDescriptions(); + var missingValues = enumValues.Where(value => !dictionaryValues.ContainsKey(value)).ToList(); + missingValues.Should().HaveCount(0, "BuildCryptoErrorDescriptions() should contain all cases from CryptoError enum."); + } + } +}