From 198b9d59b6c87addb03ef9ec5c48fd85c5104e61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Milkovi=C4=8D?= Date: Tue, 30 Jan 2024 01:54:05 +0100 Subject: [PATCH] Update authenticode parser This updates authenticode parser to the latest versions which contains sereral fixes which accumulated over time. * Several time_t variables replaced with int64_t * RFC5652 countersignatures are now correctly parsed * Memory corruption related fixes where authenticode-parser would crash otherwise --- .../authenticode-parser/authenticode.h | 8 +- .../pe/authenticode-parser/authenticode.c | 1063 ++++++++--------- .../pe/authenticode-parser/certificate.c | 609 +++++----- .../pe/authenticode-parser/certificate.h | 21 +- .../pe/authenticode-parser/countersignature.c | 851 ++++++++----- .../pe/authenticode-parser/countersignature.h | 33 +- .../modules/pe/authenticode-parser/helper.c | 68 +- .../modules/pe/authenticode-parser/helper.h | 27 +- .../modules/pe/authenticode-parser/structs.c | 74 +- .../modules/pe/authenticode-parser/structs.h | 91 +- 10 files changed, 1471 insertions(+), 1374 deletions(-) diff --git a/libyara/include/authenticode-parser/authenticode.h b/libyara/include/authenticode-parser/authenticode.h index 4dfe8a2d20..88f96a3cd9 100644 --- a/libyara/include/authenticode-parser/authenticode.h +++ b/libyara/include/authenticode-parser/authenticode.h @@ -106,8 +106,8 @@ typedef struct { char* key_alg; /* Name of the key algorithm */ char* sig_alg; /* Name of the signature algorithm */ char* sig_alg_oid; /* OID of the signature algorithm */ - time_t not_before; /* NotBefore validity */ - time_t not_after; /* NotAfter validity */ + int64_t not_before; /* NotBefore validity */ + int64_t not_after; /* NotAfter validity */ char* key; /* PEM encoded public key */ Attributes issuer_attrs; /* Parsed X509 Attributes of Issuer */ Attributes subject_attrs; /* Parsed X509 Attributes of Subject */ @@ -120,7 +120,7 @@ typedef struct { typedef struct { int verify_flags; /* COUNTERISGNATURE_VFY_ flag */ - time_t sign_time; /* Signing time of the timestamp countersignature */ + int64_t sign_time; /* Signing time of the timestamp countersignature */ char* digest_alg; /* Name of the digest algorithm used */ ByteArray digest; /* Stored message digest */ CertificateArray* chain; /* Certificate chain of the signer */ @@ -190,7 +190,7 @@ AuthenticodeArray* parse_authenticode(const uint8_t* pe_data, uint64_t pe_len); * @param len * @return AuthenticodeArray* */ -AuthenticodeArray* authenticode_new(const uint8_t* data, long len); +AuthenticodeArray* authenticode_new(const uint8_t* data, int32_t len); /** * @brief Deallocates AuthenticodeArray and all it's allocated members diff --git a/libyara/modules/pe/authenticode-parser/authenticode.c b/libyara/modules/pe/authenticode-parser/authenticode.c index a6a92b52af..e8a0d596ee 100644 --- a/libyara/modules/pe/authenticode-parser/authenticode.c +++ b/libyara/modules/pe/authenticode-parser/authenticode.c @@ -22,6 +22,7 @@ SOFTWARE. #include #include #include +#include #include #include #include @@ -43,443 +44,388 @@ SOFTWARE. /* Moves signatures from src to dst, returns 0 on success, * else 1. If error occurs, arguments are unchanged */ -static int authenticode_array_move( - AuthenticodeArray* dst, - AuthenticodeArray* src) +static int authenticode_array_move(AuthenticodeArray* dst, AuthenticodeArray* src) { - size_t newCount = dst->count + src->count; + size_t newCount = dst->count + src->count; - Authenticode** tmp = (Authenticode**) realloc( - dst->signatures, newCount * sizeof(Authenticode*)); - if (!tmp) - return 1; + Authenticode** tmp = (Authenticode**)realloc(dst->signatures, newCount * sizeof(Authenticode*)); + if (!tmp) + return 1; - dst->signatures = tmp; + dst->signatures = tmp; - for (size_t i = 0; i < src->count; ++i) - dst->signatures[i + dst->count] = src->signatures[i]; + for (size_t i = 0; i < src->count; ++i) + dst->signatures[i + dst->count] = src->signatures[i]; - dst->count = newCount; + dst->count = newCount; - free(src->signatures); - src->signatures = NULL; - src->count = 0; + free(src->signatures); + src->signatures = NULL; + src->count = 0; - return 0; + return 0; } static SpcIndirectDataContent* get_content(PKCS7* content) { - if (!content) - return NULL; + if (!content) + return NULL; - if (OBJ_obj2nid(content->type) != OBJ_txt2nid(NID_spc_indirect_data)) - return NULL; + if (OBJ_obj2nid(content->type) != OBJ_txt2nid(NID_spc_indirect_data)) + return NULL; - SpcIndirectDataContent* spcContent = SpcIndirectDataContent_new(); - if (!spcContent) - return NULL; + SpcIndirectDataContent* spcContent = SpcIndirectDataContent_new(); + if (!spcContent) + return NULL; - int len = content->d.other->value.sequence->length; - const uint8_t* data = content->d.other->value.sequence->data; + int len = content->d.other->value.sequence->length; + const uint8_t* data = content->d.other->value.sequence->data; - d2i_SpcIndirectDataContent(&spcContent, &data, len); + d2i_SpcIndirectDataContent(&spcContent, &data, len); - return spcContent; + return spcContent; } static char* parse_program_name(ASN1_TYPE* spcAttr) { - const uint8_t* spcData = spcAttr->value.sequence->data; - int spcLen = spcAttr->value.sequence->length; - SpcSpOpusInfo* spcInfo = d2i_SpcSpOpusInfo(NULL, &spcData, spcLen); - if (!spcInfo) - return NULL; - - char* result = NULL; - - if (spcInfo->programName) - { - uint8_t* data = NULL; - /* Should be Windows UTF16..., try to convert it to UTF8 */ - int nameLen = ASN1_STRING_to_UTF8( - &data, spcInfo->programName->value.unicode); - if (nameLen >= 0 && nameLen < spcLen) - { - result = (char*) malloc(nameLen + 1); - if (result) - { - memcpy(result, data, nameLen); - result[nameLen] = 0; - } - OPENSSL_free(data); + const uint8_t* spcData = spcAttr->value.sequence->data; + int spcLen = spcAttr->value.sequence->length; + SpcSpOpusInfo* spcInfo = d2i_SpcSpOpusInfo(NULL, &spcData, spcLen); + if (!spcInfo) + return NULL; + + char* result = NULL; + + if (spcInfo->programName) { + uint8_t* data = NULL; + /* Should be Windows UTF16..., try to convert it to UTF8 */ + int nameLen = ASN1_STRING_to_UTF8(&data, spcInfo->programName->value.unicode); + if (nameLen >= 0 && nameLen < spcLen) { + result = (char*)malloc(nameLen + 1); + if (result) { + memcpy(result, data, nameLen); + result[nameLen] = 0; + } + OPENSSL_free(data); + } } - } - SpcSpOpusInfo_free(spcInfo); - return result; + SpcSpOpusInfo_free(spcInfo); + return result; } -/* Parses X509* certs into internal representation and inserts into - * CertificateArray Array is assumed to have enough space to hold all - * certificates storted in the STACK */ -static void parse_certificates( - const STACK_OF(X509) * certs, - CertificateArray* result) +/* Parses X509* certs into internal representation and inserts into CertificateArray + * Array is assumed to have enough space to hold all certificates storted in the STACK */ +static void parse_certificates(const STACK_OF(X509) * certs, CertificateArray* result) { - int certCount = sk_X509_num(certs); - int i = 0; - for (; i < certCount; ++i) - { - Certificate* cert = certificate_new(sk_X509_value(certs, i)); - if (!cert) - break; - - /* Write to the result */ - result->certs[i] = cert; - } - result->count = i; + int certCount = sk_X509_num(certs); + int i = 0; + for (; i < certCount; ++i) { + Certificate* cert = certificate_new(sk_X509_value(certs, i)); + if (!cert) + break; + + /* Write to the result */ + result->certs[i] = cert; + } + result->count = i; } -static void parse_nested_authenticode( - PKCS7_SIGNER_INFO* si, - AuthenticodeArray* result) +static void parse_nested_authenticode(PKCS7_SIGNER_INFO* si, AuthenticodeArray* result) { - STACK_OF(X509_ATTRIBUTE)* attrs = PKCS7_get_attributes(si); - int idx = X509at_get_attr_by_NID( - attrs, OBJ_txt2nid(NID_spc_nested_signature), -1); - X509_ATTRIBUTE* attr = X509at_get_attr(attrs, idx); - - int attrCount = X509_ATTRIBUTE_count(attr); - if (!attrCount) - return; - - /* Limit the maximum amount of nested attributes to be safe from malformed - * samples */ - attrCount = attrCount > MAX_NESTED_COUNT ? MAX_NESTED_COUNT : attrCount; - - for (int i = 0; i < attrCount; ++i) - { - ASN1_TYPE* nested = X509_ATTRIBUTE_get0_type(attr, i); - if (nested == NULL) - break; - int len = nested->value.sequence->length; - const uint8_t* data = nested->value.sequence->data; - AuthenticodeArray* auth = authenticode_new(data, len); - if (!auth) - continue; - - authenticode_array_move(result, auth); - authenticode_array_free(auth); - } + STACK_OF(X509_ATTRIBUTE)* attrs = PKCS7_get_attributes(si); + int idx = X509at_get_attr_by_NID(attrs, OBJ_txt2nid(NID_spc_nested_signature), -1); + X509_ATTRIBUTE* attr = X509at_get_attr(attrs, idx); + + int attrCount = X509_ATTRIBUTE_count(attr); + if (!attrCount) + return; + + /* Limit the maximum amount of nested attributes to be safe from malformed samples */ + attrCount = attrCount > MAX_NESTED_COUNT ? MAX_NESTED_COUNT : attrCount; + + for (int i = 0; i < attrCount; ++i) { + ASN1_TYPE* nested = X509_ATTRIBUTE_get0_type(attr, i); + if (nested == NULL) + break; + int len = nested->value.sequence->length; + const uint8_t* data = nested->value.sequence->data; + AuthenticodeArray* auth = authenticode_new(data, len); + if (!auth) + continue; + + authenticode_array_move(result, auth); + authenticode_array_free(auth); + } } static void parse_pkcs9_countersig(PKCS7* p7, Authenticode* auth) { - PKCS7_SIGNER_INFO* si = sk_PKCS7_SIGNER_INFO_value( - PKCS7_get_signer_info(p7), 0); + PKCS7_SIGNER_INFO* si = sk_PKCS7_SIGNER_INFO_value(PKCS7_get_signer_info(p7), 0); - STACK_OF(X509_ATTRIBUTE)* attrs = PKCS7_get_attributes(si); + STACK_OF(X509_ATTRIBUTE)* attrs = PKCS7_get_attributes(si); - int idx = X509at_get_attr_by_NID(attrs, NID_pkcs9_countersignature, -1); - X509_ATTRIBUTE* attr = X509at_get_attr(attrs, idx); + int idx = X509at_get_attr_by_NID(attrs, NID_pkcs9_countersignature, -1); + X509_ATTRIBUTE* attr = X509at_get_attr(attrs, idx); - int attrCount = X509_ATTRIBUTE_count(attr); - if (!attrCount) - return; + int attrCount = X509_ATTRIBUTE_count(attr); + if (!attrCount) + return; - /* Limit the maximum amount of nested attributes to be safe from malformed - * samples */ - attrCount = attrCount > MAX_NESTED_COUNT ? MAX_NESTED_COUNT : attrCount; + /* Limit the maximum amount of nested attributes to be safe from malformed samples */ + attrCount = attrCount > MAX_NESTED_COUNT ? MAX_NESTED_COUNT : attrCount; - for (int i = 0; i < attrCount; ++i) - { - ASN1_TYPE* nested = X509_ATTRIBUTE_get0_type(attr, i); - if (nested == NULL) - break; - int len = nested->value.sequence->length; - const uint8_t* data = nested->value.sequence->data; + for (int i = 0; i < attrCount; ++i) { + ASN1_TYPE* nested = X509_ATTRIBUTE_get0_type(attr, i); + if (nested == NULL) + break; + int len = nested->value.sequence->length; + const uint8_t* data = nested->value.sequence->data; - Countersignature* sig = pkcs9_countersig_new( - data, len, p7->d.sign->cert, si->enc_digest); - if (!sig) - continue; + Countersignature* sig = pkcs9_countersig_new(data, len, p7->d.sign->cert, si->enc_digest); + if (!sig) + continue; - countersignature_array_insert(auth->countersigs, sig); - } + countersignature_array_insert(auth->countersigs, sig); + } } -/* Extracts X509 certificates from MS countersignature and stores them into - * result */ -static void extract_ms_counter_certs( - const uint8_t* data, - int len, - CertificateArray* result) +/* Extracts X509 certificates from MS countersignature and stores them into result */ +static void extract_ms_counter_certs(const uint8_t* data, int len, CertificateArray* result) { - PKCS7* p7 = d2i_PKCS7(NULL, &data, len); - if (!p7) - return; - - STACK_OF(X509)* certs = p7->d.sign->cert; - CertificateArray* certArr = certificate_array_new(sk_X509_num(certs)); - if (!certArr) - { - PKCS7_free(p7); - return; - } - parse_certificates(certs, certArr); - certificate_array_move(result, certArr); - certificate_array_free(certArr); + PKCS7* p7 = d2i_PKCS7(NULL, &data, len); + if (!p7) + return; + + STACK_OF(X509)* certs = p7->d.sign->cert; + CertificateArray* certArr = certificate_array_new(sk_X509_num(certs)); + if (!certArr) { + PKCS7_free(p7); + return; + } + parse_certificates(certs, certArr); + certificate_array_move(result, certArr); + certificate_array_free(certArr); - PKCS7_free(p7); + PKCS7_free(p7); } static void parse_ms_countersig(PKCS7* p7, Authenticode* auth) { - PKCS7_SIGNER_INFO* si = sk_PKCS7_SIGNER_INFO_value( - PKCS7_get_signer_info(p7), 0); - - STACK_OF(X509_ATTRIBUTE)* attrs = PKCS7_get_attributes(si); - - int idx = X509at_get_attr_by_NID( - attrs, OBJ_txt2nid(NID_spc_ms_countersignature), -1); - X509_ATTRIBUTE* attr = X509at_get_attr(attrs, idx); - - int attrCount = X509_ATTRIBUTE_count(attr); - if (!attrCount) - return; - - /* Limit the maximum amount of nested attributes to be safe from malformed - * samples */ - attrCount = attrCount > MAX_NESTED_COUNT ? MAX_NESTED_COUNT : attrCount; - - for (int i = 0; i < attrCount; ++i) - { - ASN1_TYPE* nested = X509_ATTRIBUTE_get0_type(attr, i); - if (nested == NULL) - break; - int len = nested->value.sequence->length; - const uint8_t* data = nested->value.sequence->data; - - Countersignature* sig = ms_countersig_new(data, len, si->enc_digest); - if (!sig) - return; - - /* Because MS TimeStamp countersignature has it's own SET of certificates - * extract it back into parent signature for consistency with PKCS9 */ - countersignature_array_insert(auth->countersigs, sig); - extract_ms_counter_certs(data, len, auth->certs); - } + PKCS7_SIGNER_INFO* si = sk_PKCS7_SIGNER_INFO_value(PKCS7_get_signer_info(p7), 0); + + STACK_OF(X509_ATTRIBUTE)* attrs = PKCS7_get_attributes(si); + + int idx = X509at_get_attr_by_NID(attrs, OBJ_txt2nid(NID_spc_ms_countersignature), -1); + X509_ATTRIBUTE* attr = X509at_get_attr(attrs, idx); + + int attrCount = X509_ATTRIBUTE_count(attr); + if (!attrCount) + return; + + /* Limit the maximum amount of nested attributes to be safe from malformed samples */ + attrCount = attrCount > MAX_NESTED_COUNT ? MAX_NESTED_COUNT : attrCount; + + for (int i = 0; i < attrCount; ++i) { + ASN1_TYPE* nested = X509_ATTRIBUTE_get0_type(attr, i); + if (nested == NULL) + break; + int len = nested->value.sequence->length; + const uint8_t* data = nested->value.sequence->data; + + Countersignature* sig = ms_countersig_new(data, len, si->enc_digest); + if (!sig) + return; + + /* Because MS TimeStamp countersignature has it's own SET of certificates + * extract it back into parent signature for consistency with PKCS9 */ + countersignature_array_insert(auth->countersigs, sig); + extract_ms_counter_certs(data, len, auth->certs); + } } -static bool authenticode_verify( - PKCS7* p7, - PKCS7_SIGNER_INFO* si, - X509* signCert) +static bool authenticode_verify(PKCS7* p7, PKCS7_SIGNER_INFO* si, X509* signCert) { - const uint8_t* contentData = - p7->d.sign->contents->d.other->value.sequence->data; - long contentLen = p7->d.sign->contents->d.other->value.sequence->length; - - uint64_t version = 0; - ASN1_INTEGER_get_uint64(&version, p7->d.sign->version); - if (version == 1) - { - /* Move the pointer to the actual contents - skip OID and length */ - int pclass = 0, ptag = 0; - ASN1_get_object(&contentData, &contentLen, &ptag, &pclass, contentLen); - } + const uint8_t* contentData = p7->d.sign->contents->d.other->value.sequence->data; + long contentLen = p7->d.sign->contents->d.other->value.sequence->length; + + uint64_t version = 0; + ASN1_INTEGER_get_uint64(&version, p7->d.sign->version); + if (version == 1) { + /* Move the pointer to the actual contents - skip OID and length */ + int pclass = 0, ptag = 0; + ASN1_get_object(&contentData, &contentLen, &ptag, &pclass, contentLen); + } - BIO* contentBio = BIO_new_mem_buf(contentData, contentLen); - /* Create `digest` type BIO to calculate content digest for verification */ - BIO* p7bio = PKCS7_dataInit(p7, contentBio); + BIO* contentBio = BIO_new_mem_buf(contentData, contentLen); + /* Create `digest` type BIO to calculate content digest for verification */ + BIO* p7bio = PKCS7_dataInit(p7, contentBio); - char buf[4096]; - /* We now have to 'read' from p7bio to calculate content digest */ - while (BIO_read(p7bio, buf, sizeof(buf)) > 0) continue; + char buf[4096]; + /* We now have to 'read' from p7bio to calculate content digest */ + while (BIO_read(p7bio, buf, sizeof(buf)) > 0) + continue; - /* Pass it to the PKCS7_signatureVerify, to do the hard work for us */ - bool isValid = PKCS7_signatureVerify(p7bio, p7, si, signCert) == 1; + /* Pass it to the PKCS7_signatureVerify, to do the hard work for us */ + bool isValid = PKCS7_signatureVerify(p7bio, p7, si, signCert) == 1; - BIO_free_all(p7bio); + BIO_free_all(p7bio); - return isValid; + return isValid; } -/* Creates all the Authenticode objects so we can parse them with OpenSSL, is - * not thread-safe, needs to be called once before any multi-threading - * environmentt - https://github.com/openssl/openssl/issues/13524 */ +/* Creates all the Authenticode objects so we can parse them with OpenSSL, is not thread-safe, needs + * to be called once before any multi-threading environmentt - + * https://github.com/openssl/openssl/issues/13524 */ void initialize_authenticode_parser() { - OBJ_create( - "1.3.6.1.4.1.311.2.1.12", "spcSpOpusInfo", "SPC_SP_OPUS_INFO_OBJID"); - OBJ_create( - "1.3.6.1.4.1.311.3.3.1", - "spcMsCountersignature", - "SPC_MICROSOFT_COUNTERSIGNATURE"); - OBJ_create( - "1.3.6.1.4.1.311.2.4.1", "spcNestedSignature", "SPC_NESTED_SIGNATUREs"); - OBJ_create("1.3.6.1.4.1.311.2.1.4", "spcIndirectData", "SPC_INDIRECT_DATA"); + OBJ_create("1.3.6.1.4.1.311.2.1.12", "spcSpOpusInfo", "SPC_SP_OPUS_INFO_OBJID"); + OBJ_create("1.3.6.1.4.1.311.3.3.1", "spcMsCountersignature", "SPC_MICROSOFT_COUNTERSIGNATURE"); + OBJ_create("1.3.6.1.4.1.311.2.4.1", "spcNestedSignature", "SPC_NESTED_SIGNATUREs"); + OBJ_create("1.3.6.1.4.1.311.2.1.4", "spcIndirectData", "SPC_INDIRECT_DATA"); } -/* Return array of Authenticode signatures stored in the data, there can be - * multiple of signatures as Authenticode signatures are often nested through - * unauth attributes */ -AuthenticodeArray* authenticode_new(const uint8_t* data, long len) +/* Return array of Authenticode signatures stored in the data, there can be multiple + * of signatures as Authenticode signatures are often nested through unauth attributes */ +AuthenticodeArray* authenticode_new(const uint8_t* data, int32_t len) { - if (!data || len == 0) - return NULL; - - AuthenticodeArray* result = (AuthenticodeArray*) calloc(1, sizeof(*result)); - if (!result) - return NULL; - - result->signatures = (Authenticode**) malloc(sizeof(Authenticode*)); - if (!result->signatures) - { - free(result); - return NULL; - } - - Authenticode* auth = (Authenticode*) calloc(1, sizeof(*auth)); - if (!auth) - { - free(result->signatures); - free(result); - return NULL; - } - - result->count = 1; - result->signatures[0] = auth; - - /* Let openssl parse the PKCS7 structure */ - PKCS7* p7 = d2i_PKCS7(NULL, &data, len); - if (!p7) - { - auth->verify_flags = AUTHENTICODE_VFY_CANT_PARSE; - goto end; - } - - /* We expect SignedData type of PKCS7 */ - if (!PKCS7_type_is_signed(p7)) - { - auth->verify_flags = AUTHENTICODE_VFY_WRONG_PKCS7_TYPE; - goto end; - } - - PKCS7_SIGNED* p7data = p7->d.sign; - - uint64_t version = 0; - if (ASN1_INTEGER_get_uint64(&version, p7data->version)) - auth->version = version; - - STACK_OF(X509)* certs = p7data->cert; - - auth->certs = certificate_array_new(sk_X509_num(certs)); - if (!auth->certs) - { - auth->verify_flags = AUTHENTICODE_VFY_INTERNAL_ERROR; - goto end; - } - parse_certificates(certs, auth->certs); - - /* Get Signature content that contains the message digest and it's algorithm - */ - SpcIndirectDataContent* dataContent = get_content(p7data->contents); - if (!dataContent) - { - auth->verify_flags = AUTHENTICODE_VFY_BAD_CONTENT; - goto end; - } - - DigestInfo* messageDigest = dataContent->messageDigest; - - int digestnid = OBJ_obj2nid(messageDigest->digestAlgorithm->algorithm); - auth->digest_alg = strdup(OBJ_nid2ln(digestnid)); - - int digestLen = messageDigest->digest->length; - const uint8_t* digestData = messageDigest->digest->data; - byte_array_init(&auth->digest, digestData, digestLen); - - SpcIndirectDataContent_free(dataContent); - - Signer* signer = (Signer*) calloc(1, sizeof(Signer)); - if (!signer) - { - auth->verify_flags = AUTHENTICODE_VFY_INTERNAL_ERROR; - goto end; - } - auth->signer = signer; - - /* Authenticode is supposed to have only one SignerInfo value - * that contains all information for actual signing purposes - * and nested signatures or countersignatures */ - PKCS7_SIGNER_INFO* si = sk_PKCS7_SIGNER_INFO_value( - PKCS7_get_signer_info(p7), 0); - if (!si) - { - auth->verify_flags = AUTHENTICODE_VFY_NO_SIGNER_INFO; - goto end; - } - - auth->countersigs = (CountersignatureArray*) calloc( - 1, sizeof(CountersignatureArray)); - if (!auth->countersigs) - { - auth->verify_flags = AUTHENTICODE_VFY_INTERNAL_ERROR; - goto end; - } - /* Authenticode can contain SET of nested Authenticode signatures - * and countersignatures in unauthenticated attributes */ - parse_nested_authenticode(si, result); - parse_pkcs9_countersig(p7, auth); - parse_ms_countersig(p7, auth); - - /* Get the signing certificate for the first SignerInfo */ - STACK_OF(X509)* signCertStack = PKCS7_get0_signers(p7, certs, 0); - - X509* signCert = sk_X509_value(signCertStack, 0); - if (!signCert) - { - auth->verify_flags = AUTHENTICODE_VFY_NO_SIGNER_CERT; - sk_X509_free(signCertStack); - goto end; - } + if (!data || len <= 0) + return NULL; + + AuthenticodeArray* result = (AuthenticodeArray*)calloc(1, sizeof(*result)); + if (!result) + return NULL; + + result->signatures = (Authenticode**)malloc(sizeof(Authenticode*)); + if (!result->signatures) { + free(result); + return NULL; + } + + Authenticode* auth = (Authenticode*)calloc(1, sizeof(*auth)); + if (!auth) { + free(result->signatures); + free(result); + return NULL; + } + + result->count = 1; + result->signatures[0] = auth; + + /* Let openssl parse the PKCS7 structure */ + PKCS7* p7 = d2i_PKCS7(NULL, &data, len); + if (!p7) { + auth->verify_flags = AUTHENTICODE_VFY_CANT_PARSE; + goto end; + } + + /* We expect SignedData type of PKCS7 */ + if (!PKCS7_type_is_signed(p7) || !p7->d.sign) { + auth->verify_flags = AUTHENTICODE_VFY_WRONG_PKCS7_TYPE; + goto end; + } + + PKCS7_SIGNED* p7data = p7->d.sign; + + uint64_t version = 0; + if (ASN1_INTEGER_get_uint64(&version, p7data->version)) + auth->version = version; + + STACK_OF(X509)* certs = p7data->cert; + + auth->certs = certificate_array_new(sk_X509_num(certs)); + if (!auth->certs) { + auth->verify_flags = AUTHENTICODE_VFY_INTERNAL_ERROR; + goto end; + } + parse_certificates(certs, auth->certs); + + /* Get Signature content that contains the message digest and it's algorithm */ + SpcIndirectDataContent* dataContent = get_content(p7data->contents); + if (!dataContent) { + auth->verify_flags = AUTHENTICODE_VFY_BAD_CONTENT; + goto end; + } + + DigestInfo* messageDigest = dataContent->messageDigest; + + int digestnid = OBJ_obj2nid(messageDigest->digestAlgorithm->algorithm); + auth->digest_alg = strdup(OBJ_nid2ln(digestnid)); - sk_X509_free(signCertStack); + int digestLen = messageDigest->digest->length; + const uint8_t* digestData = messageDigest->digest->data; + byte_array_init(&auth->digest, digestData, digestLen); - signer->chain = parse_signer_chain(signCert, certs); + SpcIndirectDataContent_free(dataContent); - /* Get the Signers digest of Authenticode content */ - ASN1_TYPE* digest = PKCS7_get_signed_attribute(si, NID_pkcs9_messageDigest); - if (!digest) - { - auth->verify_flags = AUTHENTICODE_VFY_DIGEST_MISSING; - goto end; - } + Signer* signer = (Signer*)calloc(1, sizeof(Signer)); + if (!signer) { + auth->verify_flags = AUTHENTICODE_VFY_INTERNAL_ERROR; + goto end; + } + auth->signer = signer; + + /* Authenticode is supposed to have only one SignerInfo value + * that contains all information for actual signing purposes + * and nested signatures or countersignatures */ + PKCS7_SIGNER_INFO* si = sk_PKCS7_SIGNER_INFO_value(PKCS7_get_signer_info(p7), 0); + if (!si) { + auth->verify_flags = AUTHENTICODE_VFY_NO_SIGNER_INFO; + goto end; + } + + auth->countersigs = (CountersignatureArray*)calloc(1, sizeof(CountersignatureArray)); + if (!auth->countersigs) { + auth->verify_flags = AUTHENTICODE_VFY_INTERNAL_ERROR; + goto end; + } + /* Authenticode can contain SET of nested Authenticode signatures + * and countersignatures in unauthenticated attributes */ + parse_nested_authenticode(si, result); + parse_pkcs9_countersig(p7, auth); + parse_ms_countersig(p7, auth); + + /* Get the signing certificate for the first SignerInfo */ + STACK_OF(X509)* signCertStack = PKCS7_get0_signers(p7, certs, 0); + + X509* signCert = sk_X509_value(signCertStack, 0); + if (!signCert) { + auth->verify_flags = AUTHENTICODE_VFY_NO_SIGNER_CERT; + sk_X509_free(signCertStack); + goto end; + } + + sk_X509_free(signCertStack); + + signer->chain = parse_signer_chain(signCert, certs); + + /* Get the Signers digest of Authenticode content */ + ASN1_TYPE* digest = PKCS7_get_signed_attribute(si, NID_pkcs9_messageDigest); + if (!digest) { + auth->verify_flags = AUTHENTICODE_VFY_DIGEST_MISSING; + goto end; + } - digestnid = OBJ_obj2nid(si->digest_alg->algorithm); - signer->digest_alg = strdup(OBJ_nid2ln(digestnid)); + digestnid = OBJ_obj2nid(si->digest_alg->algorithm); + signer->digest_alg = strdup(OBJ_nid2ln(digestnid)); - digestLen = digest->value.asn1_string->length; - digestData = digest->value.asn1_string->data; - byte_array_init(&signer->digest, digestData, digestLen); + digestLen = digest->value.asn1_string->length; + digestData = digest->value.asn1_string->data; + byte_array_init(&signer->digest, digestData, digestLen); - /* Authenticode stores optional programName in non-optional SpcSpOpusInfo - * attribute */ - ASN1_TYPE* spcInfo = PKCS7_get_signed_attribute( - si, OBJ_txt2nid(NID_spc_info)); - if (spcInfo) - signer->program_name = parse_program_name(spcInfo); + /* Authenticode stores optional programName in non-optional SpcSpOpusInfo attribute */ + ASN1_TYPE* spcInfo = PKCS7_get_signed_attribute(si, OBJ_txt2nid(NID_spc_info)); + if (spcInfo) + signer->program_name = parse_program_name(spcInfo); - /* If we got to this point, we got all we need to start verifying */ - bool isValid = authenticode_verify(p7, si, signCert); - if (!isValid) - auth->verify_flags = AUTHENTICODE_VFY_INVALID; + /* If we got to this point, we got all we need to start verifying */ + bool isValid = authenticode_verify(p7, si, signCert); + if (!isValid) + auth->verify_flags = AUTHENTICODE_VFY_INVALID; end: - PKCS7_free(p7); - return result; + PKCS7_free(p7); + return result; } static int authenticode_digest( @@ -490,227 +436,216 @@ static int authenticode_digest( uint32_t cert_table_addr, uint8_t* digest) { - uint32_t buffer_size = 0xFFFF; - uint8_t* buffer = (uint8_t*) malloc(buffer_size); - - /* BIO with the file data */ - BIO* bio = BIO_new_mem_buf(pe_data, cert_table_addr); - - EVP_MD_CTX* mdctx = EVP_MD_CTX_new(); - if (!buffer || !bio || !mdctx) - goto error; - - if (!EVP_DigestInit(mdctx, md)) - goto error; - - /* Calculate size of the space between file start and PE header */ - /* Checksum starts at 0x58th byte of the header */ - uint32_t pe_checksum_offset = pe_hdr_offset + 0x58; - /* Space between DOS and PE header could have arbitrary amount of data, read - * in chunks */ - uint32_t fpos = 0; - while (fpos < pe_checksum_offset) - { - uint32_t len_to_read = pe_checksum_offset - fpos; - if (len_to_read > buffer_size) - len_to_read = buffer_size; - - int rlen = BIO_read(bio, buffer, len_to_read); - if (rlen <= 0) - goto error; - - if (!EVP_DigestUpdate(mdctx, buffer, rlen)) - goto error; - - fpos += rlen; - } - - /* Skip the checksum */ - if (BIO_read(bio, buffer, 4) <= 0) - goto error; - - /* 64bit PE file is larger than 32bit */ - uint32_t pe64_extra = is_64bit ? 16 : 0; - - /* Read up to certificate table*/ - uint32_t cert_table_offset = 0x3c + pe64_extra; - - if (BIO_read(bio, buffer, cert_table_offset) <= 0) - goto error; - - if (!EVP_DigestUpdate(mdctx, buffer, cert_table_offset)) - goto error; - - /* Skip certificate table */ - if (BIO_read(bio, buffer, 8) <= 0) - goto error; - - /* PE header with check sum + checksum + cert table offset + cert table len */ - fpos = pe_checksum_offset + 4 + cert_table_offset + 8; - - /* Hash everything up to the signature (assuming signature is stored in the - * end of the file) */ - /* Read chunks of the file in case the file is large */ - while (fpos < cert_table_addr) - { - uint32_t len_to_read = cert_table_addr - fpos; - if (len_to_read > buffer_size) - len_to_read = buffer_size; - - int rlen = BIO_read(bio, buffer, len_to_read); - if (rlen <= 0) - goto error; - - if (!EVP_DigestUpdate(mdctx, buffer, rlen)) - goto error; - fpos += rlen; - } - - /* Calculate the digest, write it into digest */ - if (!EVP_DigestFinal(mdctx, digest, NULL)) - goto error; - - EVP_MD_CTX_free(mdctx); - BIO_free_all(bio); - free(buffer); - return 0; + uint32_t buffer_size = 0xFFFF; + uint8_t* buffer = (uint8_t*)malloc(buffer_size); + + /* BIO with the file data */ + BIO* bio = BIO_new_mem_buf(pe_data, cert_table_addr); + + EVP_MD_CTX* mdctx = EVP_MD_CTX_new(); + if (!buffer || !bio || !mdctx) + goto error; + + if (!EVP_DigestInit(mdctx, md)) + goto error; + + /* Calculate size of the space between file start and PE header */ + /* Checksum starts at 0x58th byte of the header */ + uint32_t pe_checksum_offset = pe_hdr_offset + 0x58; + /* Space between DOS and PE header could have arbitrary amount of data, read in chunks */ + uint32_t fpos = 0; + while (fpos < pe_checksum_offset) { + uint32_t len_to_read = pe_checksum_offset - fpos; + if (len_to_read > buffer_size) + len_to_read = buffer_size; + + int rlen = BIO_read(bio, buffer, len_to_read); + if (rlen <= 0) + goto error; + + if (!EVP_DigestUpdate(mdctx, buffer, rlen)) + goto error; + + fpos += rlen; + } + + /* Skip the checksum */ + if (BIO_read(bio, buffer, 4) <= 0) + goto error; + + /* 64bit PE file is larger than 32bit */ + uint32_t pe64_extra = is_64bit ? 16 : 0; + + /* Read up to certificate table*/ + uint32_t cert_table_offset = 0x3c + pe64_extra; + + if (BIO_read(bio, buffer, cert_table_offset) <= 0) + goto error; + + if (!EVP_DigestUpdate(mdctx, buffer, cert_table_offset)) + goto error; + + /* Skip certificate table */ + if (BIO_read(bio, buffer, 8) <= 0) + goto error; + + /* PE header with check sum + checksum + cert table offset + cert table len */ + fpos = pe_checksum_offset + 4 + cert_table_offset + 8; + + /* Hash everything up to the signature (assuming signature is stored in the + * end of the file) */ + /* Read chunks of the file in case the file is large */ + while (fpos < cert_table_addr) { + uint32_t len_to_read = cert_table_addr - fpos; + if (len_to_read > buffer_size) + len_to_read = buffer_size; + + int rlen = BIO_read(bio, buffer, len_to_read); + if (rlen <= 0) + goto error; + + if (!EVP_DigestUpdate(mdctx, buffer, rlen)) + goto error; + fpos += rlen; + } + + /* Calculate the digest, write it into digest */ + if (!EVP_DigestFinal(mdctx, digest, NULL)) + goto error; + + EVP_MD_CTX_free(mdctx); + BIO_free_all(bio); + free(buffer); + return 0; error: - EVP_MD_CTX_free(mdctx); - BIO_free_all(bio); - free(buffer); - return 1; + EVP_MD_CTX_free(mdctx); + BIO_free_all(bio); + free(buffer); + return 1; } AuthenticodeArray* parse_authenticode(const uint8_t* pe_data, uint64_t pe_len) { - const uint64_t dos_hdr_size = 0x40; - if (pe_len < dos_hdr_size) - return NULL; - - /* Check if it has DOS signature, so we don't parse random gibberish */ - uint8_t dos_prefix[] = {0x4d, 0x5a}; - if (memcmp(pe_data, dos_prefix, sizeof(dos_prefix)) != 0) - return NULL; - - /* offset to pointer in DOS header, that points to PE header */ - const int pe_hdr_ptr_offset = 0x3c; - /* Read the PE offset */ - uint32_t pe_offset = letoh32(*(uint32_t*) (pe_data + pe_hdr_ptr_offset)); - /* Offset to Magic, to know the PE class (32/64bit) */ - uint32_t magic_addr = pe_offset + 0x18; - - if (pe_len < magic_addr + sizeof(uint16_t)) - return NULL; - - /* Read the magic and check if we have 64bit PE */ - uint16_t magic = letoh16(*(uint16_t*) (pe_data + magic_addr)); - bool is_64bit = magic == 0x20b; - /* If PE is 64bit, header is 16 bytes larger */ - uint8_t pe64_extra = is_64bit ? 16 : 0; - - /* Calculate offset to certificate table directory */ - uint32_t pe_cert_table_addr = pe_offset + pe64_extra + 0x98; - - if (pe_len < pe_cert_table_addr + 2 * sizeof(uint32_t)) - return NULL; - - /* Use 64bit type due to the potential overflow in crafted binaries */ - uint64_t cert_addr = letoh32(*(uint32_t*) (pe_data + pe_cert_table_addr)); - uint64_t cert_len = letoh32(*(uint32_t*) (pe_data + pe_cert_table_addr + 4)); - - /* we need atleast 8 bytes to read dwLength, revision and certType */ - if (cert_len < 8 || pe_len < cert_addr + 8) - return NULL; - - uint32_t dwLength = letoh32(*(uint32_t*) (pe_data + cert_addr)); - if (pe_len < cert_addr + dwLength) - return NULL; - /* dwLength = offsetof(WIN_CERTIFICATE, bCertificate) + (size of the - * variable-length binary array contained within bCertificate) */ - AuthenticodeArray* auth_array = authenticode_new( - pe_data + cert_addr + 0x8, dwLength - 0x8); - if (!auth_array) - return NULL; - - /* Compare valid signatures file digests to actual file digest, to complete - * verification */ - for (size_t i = 0; i < auth_array->count; ++i) - { - Authenticode* sig = auth_array->signatures[i]; - - const EVP_MD* md = EVP_get_digestbyname(sig->digest_alg); - if (!md || !sig->digest.len || !sig->digest.data) - { - /* If there is an verification error, keep the first error */ - if (sig->verify_flags == AUTHENTICODE_VFY_VALID) - sig->verify_flags = AUTHENTICODE_VFY_UNKNOWN_ALGORITHM; - - continue; - } + const uint64_t dos_hdr_size = 0x40; + if (pe_len < dos_hdr_size) + return NULL; + + /* Check if it has DOS signature, so we don't parse random gibberish */ + uint8_t dos_prefix[] = {0x4d, 0x5a}; + if (memcmp(pe_data, dos_prefix, sizeof(dos_prefix)) != 0) + return NULL; + + /* offset to pointer in DOS header, that points to PE header */ + const int pe_hdr_ptr_offset = 0x3c; + /* Read the PE offset */ + uint32_t pe_offset = letoh32(*(uint32_t*)(pe_data + pe_hdr_ptr_offset)); + /* Offset to Magic, to know the PE class (32/64bit) */ + uint32_t magic_addr = pe_offset + 0x18; + + if (pe_len < magic_addr + sizeof(uint16_t)) + return NULL; + + /* Read the magic and check if we have 64bit PE */ + uint16_t magic = letoh16(*(uint16_t*)(pe_data + magic_addr)); + bool is_64bit = magic == 0x20b; + /* If PE is 64bit, header is 16 bytes larger */ + uint8_t pe64_extra = is_64bit ? 16 : 0; + + /* Calculate offset to certificate table directory */ + uint32_t pe_cert_table_addr = pe_offset + pe64_extra + 0x98; + + if (pe_len < pe_cert_table_addr + 2 * sizeof(uint32_t)) + return NULL; + + /* Use 64bit type due to the potential overflow in crafted binaries */ + uint64_t cert_addr = letoh32(*(uint32_t*)(pe_data + pe_cert_table_addr)); + uint64_t cert_len = letoh32(*(uint32_t*)(pe_data + pe_cert_table_addr + 4)); + + /* we need atleast 8 bytes to read dwLength, revision and certType */ + if (cert_len < 8 || pe_len < cert_addr + 8) + return NULL; + + uint32_t dwLength = letoh32(*(uint32_t*)(pe_data + cert_addr)); + if (pe_len < cert_addr + dwLength) + return NULL; + /* dwLength = offsetof(WIN_CERTIFICATE, bCertificate) + (size of the variable-length binary + * array contained within bCertificate) */ + AuthenticodeArray* auth_array = authenticode_new(pe_data + cert_addr + 0x8, dwLength - 0x8); + if (!auth_array) + return NULL; + + /* Compare valid signatures file digests to actual file digest, to complete verification */ + for (size_t i = 0; i < auth_array->count; ++i) { + Authenticode* sig = auth_array->signatures[i]; + + const EVP_MD* md = EVP_get_digestbyname(sig->digest_alg); + if (!md || !sig->digest.len || !sig->digest.data) { + /* If there is an verification error, keep the first error */ + if (sig->verify_flags == AUTHENTICODE_VFY_VALID) + sig->verify_flags = AUTHENTICODE_VFY_UNKNOWN_ALGORITHM; + + continue; + } #if OPENSSL_VERSION_NUMBER >= 0x3000000fL - int mdlen = EVP_MD_get_size(md); + int mdlen = EVP_MD_get_size(md); #else - int mdlen = EVP_MD_size(md); + int mdlen = EVP_MD_size(md); #endif - sig->file_digest.len = mdlen; - sig->file_digest.data = (uint8_t*) malloc(mdlen); - if (!sig->file_digest.data) - continue; - - if (authenticode_digest( - md, pe_data, pe_offset, is_64bit, cert_addr, sig->file_digest.data)) - { - /* If there is an verification error, keep the first error */ - if (sig->verify_flags == AUTHENTICODE_VFY_VALID) - sig->verify_flags = AUTHENTICODE_VFY_INTERNAL_ERROR; - break; + sig->file_digest.len = mdlen; + sig->file_digest.data = (uint8_t*)malloc(mdlen); + if (!sig->file_digest.data) + continue; + + if (authenticode_digest( + md, pe_data, pe_offset, is_64bit, cert_addr, sig->file_digest.data)) { + + /* If there is an verification error, keep the first error */ + if (sig->verify_flags == AUTHENTICODE_VFY_VALID) + sig->verify_flags = AUTHENTICODE_VFY_INTERNAL_ERROR; + break; + } + + /* Complete the verification */ + if (memcmp(sig->file_digest.data, sig->digest.data, mdlen) != 0) + sig->verify_flags = AUTHENTICODE_VFY_WRONG_FILE_DIGEST; } - /* Complete the verification */ - if (memcmp(sig->file_digest.data, sig->digest.data, mdlen) != 0) - sig->verify_flags = AUTHENTICODE_VFY_WRONG_FILE_DIGEST; - } - - return auth_array; + return auth_array; } static void signer_free(Signer* si) { - if (si) - { - free(si->digest.data); - free(si->digest_alg); - free(si->program_name); - certificate_array_free(si->chain); - free(si); - } + if (si) { + free(si->digest.data); + free(si->digest_alg); + free(si->program_name); + certificate_array_free(si->chain); + free(si); + } } static void authenticode_free(Authenticode* auth) { - if (auth) - { - free(auth->digest.data); - free(auth->file_digest.data); - free(auth->digest_alg); - signer_free(auth->signer); - certificate_array_free(auth->certs); - countersignature_array_free(auth->countersigs); - free(auth); - } + if (auth) { + free(auth->digest.data); + free(auth->file_digest.data); + free(auth->digest_alg); + signer_free(auth->signer); + certificate_array_free(auth->certs); + countersignature_array_free(auth->countersigs); + free(auth); + } } void authenticode_array_free(AuthenticodeArray* arr) { - if (arr) - { - for (size_t i = 0; i < arr->count; ++i) - { - authenticode_free(arr->signatures[i]); + if (arr) { + for (size_t i = 0; i < arr->count; ++i) { + authenticode_free(arr->signatures[i]); + } + free(arr->signatures); + free(arr); } - free(arr->signatures); - free(arr); - } } diff --git a/libyara/modules/pe/authenticode-parser/certificate.c b/libyara/modules/pe/authenticode-parser/certificate.c index 3a95d0c124..cf688e77b2 100644 --- a/libyara/modules/pe/authenticode-parser/certificate.c +++ b/libyara/modules/pe/authenticode-parser/certificate.c @@ -24,6 +24,7 @@ SOFTWARE. #include #include #include +#include #include #include #include @@ -35,410 +36,386 @@ SOFTWARE. from OpenSSL 3.0 */ static void parse_oneline_string(char* string) { - size_t len = strlen(string); - char* tmp = string; - while (true) - { - char* ptr = strstr(tmp, "\\/"); - if (!ptr) - break; - - memmove(ptr, ptr + 1, strlen(ptr + 1)); - tmp = ptr + 1; - len--; - } - - string[len] = 0; + size_t len = strlen(string); + char* tmp = string; + while (true) { + char* ptr = strstr(tmp, "\\/"); + if (!ptr) + break; + + memmove(ptr, ptr + 1, strlen(ptr + 1)); + tmp = ptr + 1; + len--; + } + + string[len] = 0; } #endif static void parse_name_attributes(X509_NAME* raw, Attributes* attr) { - if (!raw || !attr) - return; - - int entryCount = X509_NAME_entry_count(raw); - for (int i = entryCount - 1; i >= 0; --i) - { - X509_NAME_ENTRY* entryName = X509_NAME_get_entry(raw, i); - ASN1_STRING* asn1String = X509_NAME_ENTRY_get_data(entryName); - - const char* key = OBJ_nid2sn( - OBJ_obj2nid(X509_NAME_ENTRY_get_object(entryName))); - - ByteArray array = {0}; - if (byte_array_init(&array, asn1String->data, asn1String->length) == -1) - break; - - if (strcmp(key, "C") == 0 && !attr->country.data) - attr->country = array; - else if (strcmp(key, "O") == 0 && !attr->organization.data) - attr->organization = array; - else if (strcmp(key, "OU") == 0 && !attr->organizationalUnit.data) - attr->organizationalUnit = array; - else if (strcmp(key, "dnQualifier") == 0 && !attr->nameQualifier.data) - attr->nameQualifier = array; - else if (strcmp(key, "ST") == 0 && !attr->state.data) - attr->state = array; - else if (strcmp(key, "CN") == 0 && !attr->commonName.data) - attr->commonName = array; - else if (strcmp(key, "serialNumber") == 0 && !attr->serialNumber.data) - attr->serialNumber = array; - else if (strcmp(key, "L") == 0 && !attr->locality.data) - attr->locality = array; - else if (strcmp(key, "title") == 0 && !attr->title.data) - attr->title = array; - else if (strcmp(key, "SN") == 0 && !attr->surname.data) - attr->surname = array; - else if (strcmp(key, "GN") == 0 && !attr->givenName.data) - attr->givenName = array; - else if (strcmp(key, "initials") == 0 && !attr->initials.data) - attr->initials = array; - else if (strcmp(key, "pseudonym") == 0 && !attr->pseudonym.data) - attr->pseudonym = array; - else if ( - strcmp(key, "generationQualifier") == 0 && - !attr->generationQualifier.data) - attr->generationQualifier = array; - else if (strcmp(key, "emailAddress") == 0 && !attr->emailAddress.data) - attr->emailAddress = array; - else - free(array.data); - } + if (!raw || !attr) + return; + + int entryCount = X509_NAME_entry_count(raw); + for (int i = entryCount - 1; i >= 0; --i) { + X509_NAME_ENTRY* entryName = X509_NAME_get_entry(raw, i); + ASN1_STRING* asn1String = X509_NAME_ENTRY_get_data(entryName); + + const char* key = OBJ_nid2sn(OBJ_obj2nid(X509_NAME_ENTRY_get_object(entryName))); + + ByteArray array = {0}; + if (byte_array_init(&array, asn1String->data, asn1String->length) == -1) + break; + + if (strcmp(key, "C") == 0 && !attr->country.data) + attr->country = array; + else if (strcmp(key, "O") == 0 && !attr->organization.data) + attr->organization = array; + else if (strcmp(key, "OU") == 0 && !attr->organizationalUnit.data) + attr->organizationalUnit = array; + else if (strcmp(key, "dnQualifier") == 0 && !attr->nameQualifier.data) + attr->nameQualifier = array; + else if (strcmp(key, "ST") == 0 && !attr->state.data) + attr->state = array; + else if (strcmp(key, "CN") == 0 && !attr->commonName.data) + attr->commonName = array; + else if (strcmp(key, "serialNumber") == 0 && !attr->serialNumber.data) + attr->serialNumber = array; + else if (strcmp(key, "L") == 0 && !attr->locality.data) + attr->locality = array; + else if (strcmp(key, "title") == 0 && !attr->title.data) + attr->title = array; + else if (strcmp(key, "SN") == 0 && !attr->surname.data) + attr->surname = array; + else if (strcmp(key, "GN") == 0 && !attr->givenName.data) + attr->givenName = array; + else if (strcmp(key, "initials") == 0 && !attr->initials.data) + attr->initials = array; + else if (strcmp(key, "pseudonym") == 0 && !attr->pseudonym.data) + attr->pseudonym = array; + else if (strcmp(key, "generationQualifier") == 0 && !attr->generationQualifier.data) + attr->generationQualifier = array; + else if (strcmp(key, "emailAddress") == 0 && !attr->emailAddress.data) + attr->emailAddress = array; + else + free(array.data); + } } /* Reconstructs signers certificate chain */ CertificateArray* parse_signer_chain(X509* signCert, STACK_OF(X509) * certs) { - if (!signCert || !certs) - return NULL; + if (!signCert || !certs) + return NULL; - X509_STORE* store = X509_STORE_new(); - if (!store) - return NULL; + X509_STORE* store = X509_STORE_new(); + if (!store) + return NULL; - X509_STORE_CTX* storeCtx = X509_STORE_CTX_new(); - if (!storeCtx) - { - X509_STORE_CTX_free(storeCtx); - return NULL; - } + X509_STORE_CTX* storeCtx = X509_STORE_CTX_new(); + if (!storeCtx) { + X509_STORE_CTX_free(storeCtx); + return NULL; + } - X509_STORE_CTX_init(storeCtx, store, signCert, certs); + X509_STORE_CTX_init(storeCtx, store, signCert, certs); - /* I can't find ability to use this function for static verification with - * missing trust anchors, because roots are generally not part of the PKCS7 - * signatures, so the return value is currently ignored and the function is - * only used to build the certificate chain */ - X509_verify_cert(storeCtx); + /* I can't find ability to use this function for static verification with missing trust anchors, + * because roots are generally not part of the PKCS7 signatures, so the return value is + * currently ignored and the function is only used to build the certificate chain */ + X509_verify_cert(storeCtx); - STACK_OF(X509)* chain = X509_STORE_CTX_get_chain(storeCtx); + STACK_OF(X509)* chain = X509_STORE_CTX_get_chain(storeCtx); - int certCount = sk_X509_num(chain); + int certCount = sk_X509_num(chain); - CertificateArray* result = (CertificateArray*) calloc(1, sizeof(*result)); - if (!result) - goto error; + CertificateArray* result = (CertificateArray*)calloc(1, sizeof(*result)); + if (!result) + goto error; - result->certs = (Certificate**) calloc(certCount, sizeof(Certificate*)); - if (!result->certs && certCount > 0) - goto error; + result->certs = (Certificate**)calloc(certCount, sizeof(Certificate*)); + if (!result->certs) + goto error; - /* Convert each certificate to internal representation */ - for (int i = 0; i < certCount; ++i) - { - Certificate* cert = certificate_new(sk_X509_value(chain, i)); - if (!cert) - goto error; + /* Convert each certificate to internal representation */ + for (int i = 0; i < certCount; ++i) { + Certificate* cert = certificate_new(sk_X509_value(chain, i)); + if (!cert) + goto error; - result->certs[i] = cert; - result->count++; - } + result->certs[i] = cert; + result->count++; + } - X509_STORE_free(store); - X509_STORE_CTX_free(storeCtx); - return result; + X509_STORE_free(store); + X509_STORE_CTX_free(storeCtx); + return result; error: /* In case of error, return nothing */ - if (result) - { - for (size_t i = 0; i < result->count; ++i) - { - certificate_free(result->certs[i]); + if (result) { + for (size_t i = 0; i < result->count; ++i) { + certificate_free(result->certs[i]); + } + free(result->certs); + free(result); } - free(result->certs); - free(result); - } - X509_STORE_free(store); - X509_STORE_CTX_free(storeCtx); + X509_STORE_free(store); + X509_STORE_CTX_free(storeCtx); - return NULL; + return NULL; } /* Taken from YARA for compatibility */ static char* integer_to_serial(ASN1_INTEGER* serial) { - int bytes = i2d_ASN1_INTEGER(serial, NULL); - - char* res = NULL; - /* According to X.509 specification the maximum length for the - * serial number is 20 octets. Add two bytes to account for - * DER type and length information. */ - if (bytes < 2 || bytes > 22) - return NULL; - - /* Now that we know the size of the serial number allocate enough - * space to hold it, and use i2d_ASN1_INTEGER() one last time to - * hold it in the allocated buffer. */ - uint8_t* serial_der = (uint8_t*) malloc(bytes); - if (!serial_der) - return NULL; - - uint8_t* serial_bytes; - - bytes = i2d_ASN1_INTEGER(serial, &serial_der); - - /* i2d_ASN1_INTEGER() moves the pointer as it writes into - serial_bytes. Move it back. */ - serial_der -= bytes; - - /* Skip over DER type, length information */ - serial_bytes = serial_der + 2; - bytes -= 2; - - /* Also allocate space to hold the "common" string format: - * 00:01:02:03:04... - * - * For each byte in the serial to convert to hexlified format we - * need three bytes, two for the byte itself and one for colon. - * The last one doesn't have the colon, but the extra byte is used - * for the NULL terminator. */ - res = (char*) malloc(bytes * 3); - if (res) - { - for (int i = 0; i < bytes; i++) - { - /* Don't put the colon on the last one. */ - if (i < bytes - 1) - snprintf(res + 3 * i, 4, "%02x:", serial_bytes[i]); - else - snprintf(res + 3 * i, 3, "%02x", serial_bytes[i]); + int bytes = i2d_ASN1_INTEGER(serial, NULL); + + char* res = NULL; + /* According to X.509 specification the maximum length for the + * serial number is 20 octets. Add two bytes to account for + * DER type and length information. */ + if (bytes < 2 || bytes > 22) + return NULL; + + /* Now that we know the size of the serial number allocate enough + * space to hold it, and use i2d_ASN1_INTEGER() one last time to + * hold it in the allocated buffer. */ + uint8_t* serial_der = (uint8_t*)malloc(bytes); + if (!serial_der) + return NULL; + + uint8_t* serial_bytes; + + bytes = i2d_ASN1_INTEGER(serial, &serial_der); + + /* i2d_ASN1_INTEGER() moves the pointer as it writes into + serial_bytes. Move it back. */ + serial_der -= bytes; + + /* Skip over DER type, length information */ + serial_bytes = serial_der + 2; + bytes -= 2; + + /* Also allocate space to hold the "common" string format: + * 00:01:02:03:04... + * + * For each byte in the serial to convert to hexlified format we + * need three bytes, two for the byte itself and one for colon. + * The last one doesn't have the colon, but the extra byte is used + * for the NULL terminator. */ + res = (char*)malloc(bytes * 3); + if (res) { + for (int i = 0; i < bytes; i++) { + /* Don't put the colon on the last one. */ + if (i < bytes - 1) + snprintf(res + 3 * i, 4, "%02x:", serial_bytes[i]); + else + snprintf(res + 3 * i, 3, "%02x", serial_bytes[i]); + } } - } - free(serial_der); + free(serial_der); - return (char*) res; + return (char*)res; } /* Converts the pubkey to pem, which is just * Base64 encoding of the DER representation */ static char* pubkey_to_pem(EVP_PKEY* pubkey) { - uint8_t* der = NULL; - int len = i2d_PUBKEY(pubkey, &der); /* Convert to DER */ - if (len <= 0) - return NULL; + uint8_t* der = NULL; + int len = i2d_PUBKEY(pubkey, &der); /* Convert to DER */ + if (len <= 0) + return NULL; + + /* Approximate the result length (padding, newlines, 4 out bytes for every 3 in) */ + uint8_t* result = (uint8_t*)malloc(len * 3 / 2); + if (!result) { + OPENSSL_free(der); + return NULL; + } - /* Approximate the result length (padding, newlines, 4 out bytes for every 3 - * in) */ - uint8_t* result = (uint8_t*) malloc(len * 3 / 2); - if (!result) - { - OPENSSL_free(der); - return NULL; - } + /* Base64 encode the DER data */ + EVP_ENCODE_CTX* ctx = EVP_ENCODE_CTX_new(); + if (!ctx) { + OPENSSL_free(der); + free(result); + return NULL; + } + + int resultLen = 0; + int tmp = 0; + EVP_EncodeInit(ctx); + EVP_EncodeUpdate(ctx, result, &tmp, der, len); + resultLen += tmp; + EVP_EncodeFinal(ctx, result + resultLen, &tmp); + resultLen += tmp; - /* Base64 encode the DER data */ - EVP_ENCODE_CTX* ctx = EVP_ENCODE_CTX_new(); - if (!ctx) - { + EVP_ENCODE_CTX_free(ctx); OPENSSL_free(der); - free(result); - return NULL; - } - - int resultLen = 0; - int tmp = 0; - EVP_EncodeInit(ctx); - EVP_EncodeUpdate(ctx, result, &tmp, der, len); - resultLen += tmp; - EVP_EncodeFinal(ctx, result + resultLen, &tmp); - resultLen += tmp; - - EVP_ENCODE_CTX_free(ctx); - OPENSSL_free(der); - - /* Remove all newlines from the encoded base64 - * resultLen is excluding NULL terminator */ - for (int i = 0; result[i] != 0; i++) - { - if (result[i] == '\n') - memmove(result + i, result + i + 1, resultLen - i); - } - - return (char*) result; + + /* Remove all newlines from the encoded base64 + * resultLen is excluding NULL terminator */ + for (int i = 0; result[i] != 0; i++) { + if (result[i] == '\n') + memmove(result + i, result + i + 1, resultLen - i); + } + + return (char*)result; } Certificate* certificate_new(X509* x509) { - Certificate* result = (Certificate*) calloc(1, sizeof(*result)); - if (!result) - return NULL; + Certificate* result = (Certificate*)calloc(1, sizeof(*result)); + if (!result) + return NULL; + + /* Calculate SHA1 and SHA256 digests of the X509 structure */ + result->sha1.data = (uint8_t*)malloc(SHA_DIGEST_LENGTH); + if (result->sha1.data) { + X509_digest(x509, EVP_sha1(), result->sha1.data, NULL); + result->sha1.len = SHA_DIGEST_LENGTH; + } - /* Calculate SHA1 and SHA256 digests of the X509 structure */ - result->sha1.data = (uint8_t*) malloc(SHA_DIGEST_LENGTH); - if (result->sha1.data) - { - X509_digest(x509, EVP_sha1(), result->sha1.data, NULL); - result->sha1.len = SHA_DIGEST_LENGTH; - } - - result->sha256.data = (uint8_t*) malloc(SHA256_DIGEST_LENGTH); - if (result->sha256.data) - { - X509_digest(x509, EVP_sha256(), result->sha256.data, NULL); - result->sha256.len = SHA256_DIGEST_LENGTH; - } - - /* 256 bytes should be enough for any name */ - char buffer[256]; - - /* X509_NAME_online is deprecated and shouldn't be used per OpenSSL docs - * but we want to comply with existing YARA code */ - X509_NAME* issuerName = X509_get_issuer_name(x509); - X509_NAME_oneline(issuerName, buffer, sizeof(buffer)); - - result->issuer = strdup(buffer); - /* This is a little ugly hack for 3.0 compatibility */ + result->sha256.data = (uint8_t*)malloc(SHA256_DIGEST_LENGTH); + if (result->sha256.data) { + X509_digest(x509, EVP_sha256(), result->sha256.data, NULL); + result->sha256.len = SHA256_DIGEST_LENGTH; + } + + /* 256 bytes should be enough for any name */ + char buffer[256]; + + /* X509_NAME_online is deprecated and shouldn't be used per OpenSSL docs + * but we want to comply with existing YARA code */ + X509_NAME* issuerName = X509_get_issuer_name(x509); + X509_NAME_oneline(issuerName, buffer, sizeof(buffer)); + + result->issuer = strdup(buffer); + /* This is a little ugly hack for 3.0 compatibility */ #if OPENSSL_VERSION_NUMBER >= 0x3000000fL - parse_oneline_string(result->issuer); + parse_oneline_string(result->issuer); #endif - X509_NAME* subjectName = X509_get_subject_name(x509); - X509_NAME_oneline(subjectName, buffer, sizeof(buffer)); - result->subject = strdup(buffer); + X509_NAME* subjectName = X509_get_subject_name(x509); + X509_NAME_oneline(subjectName, buffer, sizeof(buffer)); + result->subject = strdup(buffer); #if OPENSSL_VERSION_NUMBER >= 0x3000000fL - parse_oneline_string(result->subject); + parse_oneline_string(result->subject); #endif - parse_name_attributes(issuerName, &result->issuer_attrs); - parse_name_attributes(subjectName, &result->subject_attrs); + parse_name_attributes(issuerName, &result->issuer_attrs); + parse_name_attributes(subjectName, &result->subject_attrs); - result->version = X509_get_version(x509); - result->serial = integer_to_serial(X509_get_serialNumber(x509)); - result->not_after = ASN1_TIME_to_time_t(X509_get0_notAfter(x509)); - result->not_before = ASN1_TIME_to_time_t(X509_get0_notBefore(x509)); - int sig_nid = X509_get_signature_nid(x509); - result->sig_alg = strdup(OBJ_nid2ln(sig_nid)); + result->version = X509_get_version(x509); + result->serial = integer_to_serial(X509_get_serialNumber(x509)); + result->not_after = ASN1_TIME_to_int64_t(X509_get0_notAfter(x509)); + result->not_before = ASN1_TIME_to_int64_t(X509_get0_notBefore(x509)); + int sig_nid = X509_get_signature_nid(x509); + result->sig_alg = strdup(OBJ_nid2ln(sig_nid)); - OBJ_obj2txt(buffer, sizeof(buffer), OBJ_nid2obj(sig_nid), 1); - result->sig_alg_oid = strdup(buffer); + OBJ_obj2txt(buffer, sizeof(buffer), OBJ_nid2obj(sig_nid), 1); + result->sig_alg_oid = strdup(buffer); - EVP_PKEY* pkey = X509_get0_pubkey(x509); - if (pkey) - { - result->key = pubkey_to_pem(pkey); + EVP_PKEY* pkey = X509_get0_pubkey(x509); + if (pkey) { + result->key = pubkey_to_pem(pkey); #if OPENSSL_VERSION_NUMBER >= 0x3000000fL - result->key_alg = strdup(OBJ_nid2sn(EVP_PKEY_get_base_id(pkey))); + result->key_alg = strdup(OBJ_nid2sn(EVP_PKEY_get_base_id(pkey))); #else - result->key_alg = strdup(OBJ_nid2sn(EVP_PKEY_base_id(pkey))); + result->key_alg = strdup(OBJ_nid2sn(EVP_PKEY_base_id(pkey))); #endif - } + } - return result; + return result; } /* Moves certificates from src to dst, returns 0 on success, * else 1. If error occurs, arguments are unchanged */ int certificate_array_move(CertificateArray* dst, CertificateArray* src) { - size_t newCount = dst->count + src->count; + size_t newCount = dst->count + src->count; - Certificate** tmp = (Certificate**) realloc( - dst->certs, newCount * sizeof(Certificate*)); - if (!tmp) - return 1; + Certificate** tmp = (Certificate**)realloc(dst->certs, newCount * sizeof(Certificate*)); + if (!tmp) + return 1; - dst->certs = tmp; + dst->certs = tmp; - for (size_t i = 0; i < src->count; ++i) - dst->certs[i + dst->count] = src->certs[i]; + for (size_t i = 0; i < src->count; ++i) + dst->certs[i + dst->count] = src->certs[i]; - dst->count = newCount; + dst->count = newCount; - free(src->certs); - src->certs = NULL; - src->count = 0; + free(src->certs); + src->certs = NULL; + src->count = 0; - return 0; + return 0; } /* Allocates empty certificate array with reserved space for certCount certs */ CertificateArray* certificate_array_new(int certCount) { - CertificateArray* arr = (CertificateArray*) malloc(sizeof(*arr)); - if (!arr) - return NULL; - - arr->certs = (Certificate**) malloc(sizeof(Certificate*) * certCount); - if (!arr->certs) - { - free(arr); - return NULL; - } + CertificateArray* arr = (CertificateArray*)malloc(sizeof(*arr)); + if (!arr) + return NULL; + + arr->certs = (Certificate**)malloc(sizeof(Certificate*) * certCount); + if (!arr->certs) { + free(arr); + return NULL; + } - arr->count = certCount; + arr->count = certCount; - return arr; + return arr; } -static void certificate_attributes_free(Attributes* attrs) +static void certificate_attributes_free(Attributes attrs) { - free(attrs->country.data); - free(attrs->organization.data); - free(attrs->organizationalUnit.data); - free(attrs->nameQualifier.data); - free(attrs->state.data); - free(attrs->commonName.data); - free(attrs->serialNumber.data); - free(attrs->locality.data); - free(attrs->title.data); - free(attrs->surname.data); - free(attrs->givenName.data); - free(attrs->initials.data); - free(attrs->pseudonym.data); - free(attrs->generationQualifier.data); - free(attrs->emailAddress.data); + free(attrs.country.data); + free(attrs.organization.data); + free(attrs.organizationalUnit.data); + free(attrs.nameQualifier.data); + free(attrs.state.data); + free(attrs.commonName.data); + free(attrs.serialNumber.data); + free(attrs.locality.data); + free(attrs.title.data); + free(attrs.surname.data); + free(attrs.givenName.data); + free(attrs.initials.data); + free(attrs.pseudonym.data); + free(attrs.generationQualifier.data); + free(attrs.emailAddress.data); } void certificate_free(Certificate* cert) { - if (cert) - { - free(cert->issuer); - free(cert->subject); - free(cert->sig_alg); - free(cert->sig_alg_oid); - free(cert->key_alg); - free(cert->key); - free(cert->sha1.data); - free(cert->sha256.data); - free(cert->serial); - certificate_attributes_free(&(cert->issuer_attrs)); - certificate_attributes_free(&(cert->subject_attrs)); - free(cert); - } + if (cert) { + free(cert->issuer); + free(cert->subject); + free(cert->sig_alg); + free(cert->sig_alg_oid); + free(cert->key_alg); + free(cert->key); + free(cert->sha1.data); + free(cert->sha256.data); + free(cert->serial); + certificate_attributes_free(cert->issuer_attrs); + certificate_attributes_free(cert->subject_attrs); + free(cert); + } } void certificate_array_free(CertificateArray* arr) { - if (arr) - { - for (size_t i = 0; i < arr->count; ++i) - { - certificate_free(arr->certs[i]); + if (arr) { + for (size_t i = 0; i < arr->count; ++i) { + certificate_free(arr->certs[i]); + } + free(arr->certs); + free(arr); } - free(arr->certs); - free(arr); - } } diff --git a/libyara/modules/pe/authenticode-parser/certificate.h b/libyara/modules/pe/authenticode-parser/certificate.h index 058d7b8e79..aefb797c6a 100644 --- a/libyara/modules/pe/authenticode-parser/certificate.h +++ b/libyara/modules/pe/authenticode-parser/certificate.h @@ -27,23 +27,16 @@ SOFTWARE. #include #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif - Certificate* certificate_new(X509* x509); +Certificate* certificate_new(X509* x509); +void certificate_free(Certificate* cert); - void certificate_free(Certificate* cert); - - CertificateArray* parse_signer_chain( - X509* signer_cert, - STACK_OF(X509) * certs); - - int certificate_array_move(CertificateArray* dst, CertificateArray* src); - - CertificateArray* certificate_array_new(int certCount); - - void certificate_array_free(CertificateArray* arr); +CertificateArray* parse_signer_chain(X509* signer_cert, STACK_OF(X509) * certs); +int certificate_array_move(CertificateArray* dst, CertificateArray* src); +CertificateArray* certificate_array_new(int certCount); +void certificate_array_free(CertificateArray* arr); #ifdef __cplusplus } diff --git a/libyara/modules/pe/authenticode-parser/countersignature.c b/libyara/modules/pe/authenticode-parser/countersignature.c index 7b04d70dd7..1c9ae7e02f 100644 --- a/libyara/modules/pe/authenticode-parser/countersignature.c +++ b/libyara/modules/pe/authenticode-parser/countersignature.c @@ -21,8 +21,11 @@ SOFTWARE. #include "countersignature.h" +#include +#include #include #include +#include #include #include #include @@ -36,378 +39,620 @@ SOFTWARE. #include "helper.h" #include "structs.h" +struct CountersignatureImplStruct; + +typedef TS_TST_INFO* get_ts_tst_info_func(struct CountersignatureImplStruct*); +typedef STACK_OF(X509) * get_signers_func(struct CountersignatureImplStruct*); +typedef STACK_OF(X509) * get_certs_func(struct CountersignatureImplStruct*); +typedef int +verify_digest_func(struct CountersignatureImplStruct*, uint8_t* digest, size_t digest_size); +typedef BIO* verify_signature_init_func(struct CountersignatureImplStruct*); +typedef int +verify_signature_finish_func(struct CountersignatureImplStruct*, BIO* bio, X509* signer); + +#define IMPL_FUNC_NAME(func, type) ms_countersig_impl_##func##_##type##_ + +#define DECLARE_FUNCS(type) \ + get_ts_tst_info_func IMPL_FUNC_NAME(get_ts_tst_info, type); \ + get_signers_func IMPL_FUNC_NAME(get_signers, type); \ + get_certs_func IMPL_FUNC_NAME(get_certs, type); \ + verify_digest_func IMPL_FUNC_NAME(verify_digest, type); \ + verify_signature_init_func IMPL_FUNC_NAME(verify_signature_init, type); \ + verify_signature_finish_func IMPL_FUNC_NAME(verify_signature_finish, type); + +DECLARE_FUNCS(pkcs7) +DECLARE_FUNCS(cms) + +typedef struct { + get_ts_tst_info_func* get_ts_tst_info; + get_signers_func* get_signers; + get_certs_func* get_certs; + verify_digest_func* verify_digest; + verify_signature_init_func* verify_signature_init; + verify_signature_finish_func* verify_signature_finish; +} CountersignatureImplFuncs; + +#define FUNC_ARRAY_NAME_FOR_IMPL(type) countersig_impl_funcs_##type##_ +#define FUNC_ARRAY_FOR_IMPL(type) \ + static const CountersignatureImplFuncs FUNC_ARRAY_NAME_FOR_IMPL(type) = { \ + &IMPL_FUNC_NAME(get_ts_tst_info, type), \ + &IMPL_FUNC_NAME(get_signers, type), \ + &IMPL_FUNC_NAME(get_certs, type), \ + &IMPL_FUNC_NAME(verify_digest, type), \ + &IMPL_FUNC_NAME(verify_signature_init, type), \ + &IMPL_FUNC_NAME(verify_signature_finish, type), \ + }; + +FUNC_ARRAY_FOR_IMPL(pkcs7) +FUNC_ARRAY_FOR_IMPL(cms) + +typedef enum { + CS_IMPL_PKCS7, + CS_IMPL_CMS, +} CountersignatureImplType; + +typedef struct CountersignatureImplStruct { + CountersignatureImplType type; + const CountersignatureImplFuncs* funcs; + union { + PKCS7* pkcs7; + CMS_ContentInfo* cms; + }; + // this is here to serve as a cache for CMS because the only way to obtain + // certs from CMS is to use CMS_get1_certs which leaves the deallocation + // to the caller but it just complicates things if you need to remember to + // deallocate also certs. This makes it easier if CountersignatureImpl itself + // is an owner of this thing. + STACK_OF(X509) * _certs; +} CountersignatureImpl; + Countersignature* pkcs9_countersig_new( - const uint8_t* data, - long size, - STACK_OF(X509) * certs, - ASN1_STRING* enc_digest) + const uint8_t* data, long size, STACK_OF(X509) * certs, ASN1_STRING* enc_digest) { - Countersignature* result = (Countersignature*) calloc(1, sizeof(*result)); - if (!result) - return NULL; + Countersignature* result = (Countersignature*)calloc(1, sizeof(*result)); + if (!result) + return NULL; + + PKCS7_SIGNER_INFO* si = d2i_PKCS7_SIGNER_INFO(NULL, &data, size); + if (!si) { + result->verify_flags = COUNTERSIGNATURE_VFY_CANT_PARSE; + return result; + } - PKCS7_SIGNER_INFO* si = d2i_PKCS7_SIGNER_INFO(NULL, &data, size); - if (!si) - { - result->verify_flags = COUNTERSIGNATURE_VFY_CANT_PARSE; - return result; - } - - int digestnid = OBJ_obj2nid(si->digest_alg->algorithm); - result->digest_alg = strdup(OBJ_nid2ln(digestnid)); - - const ASN1_TYPE* sign_time = PKCS7_get_signed_attribute( - si, NID_pkcs9_signingTime); - if (!sign_time) - { - result->verify_flags = COUNTERSIGNATURE_VFY_TIME_MISSING; - goto end; - } - - result->sign_time = ASN1_TIME_to_time_t(sign_time->value.utctime); - - X509* signCert = X509_find_by_issuer_and_serial( - certs, si->issuer_and_serial->issuer, si->issuer_and_serial->serial); - if (!signCert) - { - result->verify_flags = COUNTERSIGNATURE_VFY_NO_SIGNER_CERT; - goto end; - } - - /* PKCS9 stores certificates in the corresponding PKCS7 it countersigns */ - result->chain = parse_signer_chain(signCert, certs); - - /* Get digest that corresponds to decrypted encrypted digest in signature */ - ASN1_TYPE* messageDigest = PKCS7_get_signed_attribute( - si, NID_pkcs9_messageDigest); - if (!messageDigest) - { - result->verify_flags = COUNTERSIGNATURE_VFY_DIGEST_MISSING; - goto end; - } - - size_t digestLen = messageDigest->value.octet_string->length; - - if (!digestLen) - { - result->verify_flags = COUNTERSIGNATURE_VFY_DIGEST_MISSING; - goto end; - } - - const EVP_MD* md = EVP_get_digestbynid(digestnid); - if (!md) - { - result->verify_flags = COUNTERSIGNATURE_VFY_UNKNOWN_ALGORITHM; - goto end; - } - - const uint8_t* digestData = messageDigest->value.octet_string->data; - byte_array_init(&result->digest, digestData, digestLen); - - /* By this point we all necessary things for verification - * Get DER representation of the authenticated attributes to calculate its - * digest that should correspond with the one encrypted in SignerInfo */ - uint8_t* authAttrsData = NULL; - int authAttrsLen = ASN1_item_i2d( - (ASN1_VALUE*) si->auth_attr, - &authAttrsData, - ASN1_ITEM_rptr(PKCS7_ATTR_VERIFY)); - - uint8_t calc_digest[EVP_MAX_MD_SIZE]; - calculate_digest(md, authAttrsData, authAttrsLen, calc_digest); - OPENSSL_free(authAttrsData); - - /* Get public key to decrypt encrypted digest of auth attrs */ - EVP_PKEY* pkey = X509_get0_pubkey(signCert); - EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(pkey, NULL); - - /* TODO try to get rid of hardcoded length bound */ - size_t decLen = 65536; - uint8_t* decData = (uint8_t*) malloc(decLen); - if (!decData) - { - EVP_PKEY_CTX_free(ctx); - result->verify_flags = COUNTERSIGNATURE_VFY_INTERNAL_ERROR; - goto end; - } + int digestnid = OBJ_obj2nid(si->digest_alg->algorithm); + result->digest_alg = strdup(OBJ_nid2ln(digestnid)); - uint8_t* encData = si->enc_digest->data; - size_t encLen = si->enc_digest->length; + const ASN1_TYPE* sign_time = PKCS7_get_signed_attribute(si, NID_pkcs9_signingTime); + if (!sign_time) { + result->verify_flags = COUNTERSIGNATURE_VFY_TIME_MISSING; + goto end; + } - /* Decrypt the encrypted digest */ - EVP_PKEY_verify_recover_init(ctx); - bool isDecrypted = EVP_PKEY_verify_recover( - ctx, decData, &decLen, encData, encLen) == 1; - EVP_PKEY_CTX_free(ctx); + result->sign_time = ASN1_TIME_to_int64_t(sign_time->value.utctime); - if (!isDecrypted) - { - free(decData); - result->verify_flags = COUNTERSIGNATURE_VFY_CANT_DECRYPT_DIGEST; - goto end; - } + X509* signCert = X509_find_by_issuer_and_serial( + certs, si->issuer_and_serial->issuer, si->issuer_and_serial->serial); + if (!signCert) { + result->verify_flags = COUNTERSIGNATURE_VFY_NO_SIGNER_CERT; + goto end; + } + + /* PKCS9 stores certificates in the corresponding PKCS7 it countersigns */ + result->chain = parse_signer_chain(signCert, certs); + + /* Get digest that corresponds to decrypted encrypted digest in signature */ + ASN1_TYPE* messageDigest = PKCS7_get_signed_attribute(si, NID_pkcs9_messageDigest); + if (!messageDigest) { + result->verify_flags = COUNTERSIGNATURE_VFY_DIGEST_MISSING; + goto end; + } + + size_t digestLen = messageDigest->value.octet_string->length; + + if (!digestLen) { + result->verify_flags = COUNTERSIGNATURE_VFY_DIGEST_MISSING; + goto end; + } + + const EVP_MD* md = EVP_get_digestbynid(digestnid); + if (!md) { + result->verify_flags = COUNTERSIGNATURE_VFY_UNKNOWN_ALGORITHM; + goto end; + } + + const uint8_t* digestData = messageDigest->value.octet_string->data; + byte_array_init(&result->digest, digestData, digestLen); + + /* By this point we all necessary things for verification + * Get DER representation of the authenticated attributes to calculate its + * digest that should correspond with the one encrypted in SignerInfo */ + uint8_t* authAttrsData = NULL; + int authAttrsLen = ASN1_item_i2d( + (ASN1_VALUE*)si->auth_attr, &authAttrsData, ASN1_ITEM_rptr(PKCS7_ATTR_VERIFY)); + + uint8_t calc_digest[EVP_MAX_MD_SIZE]; + calculate_digest(md, authAttrsData, authAttrsLen, calc_digest); + OPENSSL_free(authAttrsData); + + /* Get public key to decrypt encrypted digest of auth attrs */ + EVP_PKEY* pkey = X509_get0_pubkey(signCert); + EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(pkey, NULL); + + /* TODO try to get rid of hardcoded length bound */ + size_t decLen = 65536; + uint8_t* decData = (uint8_t*)malloc(decLen); + if (!decData) { + EVP_PKEY_CTX_free(ctx); + result->verify_flags = COUNTERSIGNATURE_VFY_INTERNAL_ERROR; + goto end; + } + + uint8_t* encData = si->enc_digest->data; + size_t encLen = si->enc_digest->length; - /* compare the encrypted digest and calculated digest */ - bool isValid = false; + /* Decrypt the encrypted digest */ + EVP_PKEY_verify_recover_init(ctx); + bool isDecrypted = EVP_PKEY_verify_recover(ctx, decData, &decLen, encData, encLen) == 1; + EVP_PKEY_CTX_free(ctx); + + if (!isDecrypted) { + free(decData); + result->verify_flags = COUNTERSIGNATURE_VFY_CANT_DECRYPT_DIGEST; + goto end; + } + + /* compare the encrypted digest and calculated digest */ + bool isValid = false; #if OPENSSL_VERSION_NUMBER >= 0x3000000fL - size_t mdLen = EVP_MD_get_size(md); + size_t mdLen = EVP_MD_get_size(md); #else - size_t mdLen = EVP_MD_size(md); + size_t mdLen = EVP_MD_size(md); #endif - /* Sometimes signed data contains DER encoded DigestInfo structure which - * contains hash of authenticated attributes - * (39c9d136f026a9ad18fb9f41a64f76dd8418e8de625dce5d3a372bd242fc5edd) but - * other times it is just purely and I didn't find another way to distinguish - * it but only based on the length of data we get. Found mention of this in - * openssl mailing list: - * https://mta.openssl.org/pipermail/openssl-users/2015-September/002054.html - */ - if (mdLen == decLen) - { - isValid = !memcmp(calc_digest, decData, mdLen); - } - else - { - const uint8_t* data_ptr = decData; - DigestInfo* digest_info = d2i_DigestInfo(NULL, &data_ptr, decLen); - if (digest_info) - { - isValid = !memcmp(digest_info->digest->data, calc_digest, mdLen); - DigestInfo_free(digest_info); - } - else - { - isValid = false; - } - } - free(decData); - - if (!isValid) - { - result->verify_flags = COUNTERSIGNATURE_VFY_INVALID; - goto end; - } - - /* Now check the countersignature message-digest that should correspond - * to Signatures encrypted digest it countersigns */ - calculate_digest(md, enc_digest->data, enc_digest->length, calc_digest); - - /* Check if calculated one matches the stored one */ - if (digestLen != mdLen || memcmp(calc_digest, digestData, mdLen) != 0) - { - result->verify_flags = COUNTERSIGNATURE_VFY_DOESNT_MATCH_SIGNATURE; - goto end; - } + /* Sometimes signed data contains DER encoded DigestInfo structure which contains hash of + * authenticated attributes (39c9d136f026a9ad18fb9f41a64f76dd8418e8de625dce5d3a372bd242fc5edd) + * but other times it is just purely and I didn't find another way to distinguish it but only + * based on the length of data we get. Found mention of this in openssl mailing list: + * https://mta.openssl.org/pipermail/openssl-users/2015-September/002054.html */ + if (mdLen == decLen) { + isValid = !memcmp(calc_digest, decData, mdLen); + } else { + const uint8_t* data_ptr = decData; + DigestInfo* digest_info = d2i_DigestInfo(NULL, &data_ptr, decLen); + if (digest_info) { + isValid = !memcmp(digest_info->digest->data, calc_digest, mdLen); + DigestInfo_free(digest_info); + } else { + isValid = false; + } + } + free(decData); + + if (!isValid) { + result->verify_flags = COUNTERSIGNATURE_VFY_INVALID; + goto end; + } + + /* Now check the countersignature message-digest that should correspond + * to Signatures encrypted digest it countersigns */ + calculate_digest(md, enc_digest->data, enc_digest->length, calc_digest); + + /* Check if calculated one matches the stored one */ + if (digestLen != mdLen || memcmp(calc_digest, digestData, mdLen) != 0) { + result->verify_flags = COUNTERSIGNATURE_VFY_DOESNT_MATCH_SIGNATURE; + goto end; + } end: - PKCS7_SIGNER_INFO_free(si); - return result; + PKCS7_SIGNER_INFO_free(si); + return result; } -Countersignature* ms_countersig_new( - const uint8_t* data, - long size, - ASN1_STRING* enc_digest) +TS_TST_INFO* IMPL_FUNC_NAME(get_ts_tst_info, pkcs7)(CountersignatureImpl* impl) { - Countersignature* result = (Countersignature*) calloc(1, sizeof(*result)); - if (!result) - return NULL; + assert(impl->type == CS_IMPL_PKCS7); - PKCS7* p7 = d2i_PKCS7(NULL, &data, size); - if (!p7) - { - result->verify_flags = COUNTERSIGNATURE_VFY_CANT_PARSE; - return result; - } + return PKCS7_to_TS_TST_INFO(impl->pkcs7); +} - TS_TST_INFO* ts = PKCS7_to_TS_TST_INFO(p7); - if (!ts) - { - result->verify_flags = COUNTERSIGNATURE_VFY_CANT_PARSE; - PKCS7_free(p7); - return result; - } +TS_TST_INFO* IMPL_FUNC_NAME(get_ts_tst_info, cms)(CountersignatureImpl* impl) +{ + assert(impl->type == CS_IMPL_CMS); - const ASN1_TIME* rawTime = TS_TST_INFO_get_time(ts); - if (!rawTime) - { - result->verify_flags = COUNTERSIGNATURE_VFY_TIME_MISSING; - TS_TST_INFO_free(ts); - PKCS7_free(p7); - return result; - } + const ASN1_OBJECT* content_type = CMS_get0_eContentType(impl->cms); + if (!content_type || OBJ_obj2nid(content_type) != NID_id_smime_ct_TSTInfo) { + return NULL; + } - result->sign_time = ASN1_TIME_to_time_t(rawTime); + ASN1_OCTET_STRING** content = CMS_get0_content(impl->cms); + if (!content || !*content) { + return NULL; + } + + const uint8_t* data = (*content)->data; + TS_TST_INFO* ts_tst_info = d2i_TS_TST_INFO(NULL, &data, (*content)->length); + if (!ts_tst_info) { + return NULL; + } - STACK_OF(X509)* sigs = PKCS7_get0_signers(p7, p7->d.sign->cert, 0); - X509* signCert = sk_X509_value(sigs, 0); - if (!signCert) - { - result->verify_flags = COUNTERSIGNATURE_VFY_NO_SIGNER_CERT; - goto end; - } + return ts_tst_info; +} + +STACK_OF(X509) * IMPL_FUNC_NAME(get_signers, pkcs7)(CountersignatureImpl* impl) +{ + assert(impl->type == CS_IMPL_PKCS7); + + return PKCS7_get0_signers(impl->pkcs7, impl->pkcs7->d.sign->cert, 0); +} + +STACK_OF(X509) * IMPL_FUNC_NAME(get_signers, cms)(CountersignatureImpl* impl) +{ + assert(impl->type == CS_IMPL_CMS); + + STACK_OF(CMS_SignerInfo)* signer_infos = CMS_get0_SignerInfos(impl->cms); + if (!signer_infos) { + return NULL; + } - result->chain = parse_signer_chain(signCert, p7->d.sign->cert); + // Use our func points to cache the certs and don't create another copy + STACK_OF(X509)* certs = impl->funcs->get_certs(impl); + + int si_count = sk_CMS_SignerInfo_num(signer_infos); + int cert_count = certs ? sk_X509_num(certs) : 0; + STACK_OF(X509)* result = sk_X509_new_null(); + + // PKCS7_get0_signers() lets us specify the certificate array and looks up signer certificate + // there With CMS_ContentInfo, we don't have direct access to signer certificate, just all the + // certificates The only thing we can do is to go through all signer infos and find those which + // match some certificate in all certificates. It essentially simulates what + // PKCS7_get0_signers() does. + for (int i = 0; i < si_count; ++i) { + CMS_SignerInfo* si = sk_CMS_SignerInfo_value(signer_infos, i); + if (!si) { + continue; + } + + if (certs) { + for (int j = 0; j < cert_count; ++j) { + X509* cert = sk_X509_value(certs, j); + if (!cert) { + continue; + } + + if (CMS_SignerInfo_cert_cmp(si, cert) == 0) { + if (!sk_X509_push(result, cert)) { + return NULL; + } + } + } + } + } - /* Imprint == digest */ - TS_MSG_IMPRINT* imprint = TS_TST_INFO_get_msg_imprint(ts); - if (!imprint) - { - result->verify_flags = COUNTERSIGNATURE_VFY_DIGEST_MISSING; - goto end; - } + return result; +} - X509_ALGOR* digestAlg = TS_MSG_IMPRINT_get_algo(imprint); - int digestnid = OBJ_obj2nid(digestAlg->algorithm); - result->digest_alg = strdup(OBJ_nid2ln(digestnid)); +STACK_OF(X509) * IMPL_FUNC_NAME(get_certs, pkcs7)(CountersignatureImpl* impl) +{ + assert(impl->type == CS_IMPL_PKCS7); - ASN1_STRING* rawDigest = TS_MSG_IMPRINT_get_msg(imprint); + return impl->pkcs7->d.sign->cert; +} - int digestLen = rawDigest->length; - uint8_t* digestData = rawDigest->data; +STACK_OF(X509) * IMPL_FUNC_NAME(get_certs, cms)(CountersignatureImpl* impl) +{ + assert(impl->type == CS_IMPL_CMS); - byte_array_init(&result->digest, digestData, digestLen); + if (impl->_certs) { + return impl->_certs; + } - if (!digestLen) - { - result->verify_flags = COUNTERSIGNATURE_VFY_DIGEST_MISSING; - goto end; - } + impl->_certs = CMS_get1_certs(impl->cms); + return impl->_certs; +} - const EVP_MD* md = EVP_get_digestbynid(digestnid); - if (!md) - { - result->verify_flags = COUNTERSIGNATURE_VFY_UNKNOWN_ALGORITHM; - goto end; - } +int IMPL_FUNC_NAME(verify_digest, pkcs7)( + CountersignatureImpl* impl, uint8_t* digest, size_t digest_size) +{ + assert(impl->type == CS_IMPL_PKCS7); - uint8_t calc_digest[EVP_MAX_MD_SIZE]; - calculate_digest(md, enc_digest->data, enc_digest->length, calc_digest); + X509_STORE* store = X509_STORE_new(); + TS_VERIFY_CTX* ctx = TS_VERIFY_CTX_new(); + TS_VERIFY_CTX_init(ctx); + TS_VERIFY_CTX_set_flags(ctx, TS_VFY_VERSION | TS_VFY_IMPRINT); + TS_VERIFY_CTX_set_store(ctx, store); #if OPENSSL_VERSION_NUMBER >= 0x3000000fL - int mdLen = EVP_MD_get_size(md); + TS_VERIFY_CTX_set_certs(ctx, impl->funcs->get_certs(impl)); #else - int mdLen = EVP_MD_size(md); + TS_VERIFY_CTS_set_certs(ctx, impl->funcs->get_certs(impl)); #endif + TS_VERIFY_CTX_set_imprint(ctx, digest, digest_size); + + int result = TS_RESP_verify_token(ctx, impl->pkcs7); + + X509_STORE_free(store); + OPENSSL_free(ctx); + + return result; +} + +int IMPL_FUNC_NAME(verify_digest, cms)( + CountersignatureImpl* impl, uint8_t* digest, size_t digest_size) +{ + assert(impl->type == CS_IMPL_CMS); + + // This is essentially just reimplementation of TS_RESP_verify_token() from OpenSSL + TS_TST_INFO* ts_tst_info = impl->funcs->get_ts_tst_info(impl); + if (!ts_tst_info || TS_TST_INFO_get_version(ts_tst_info) != 1) { + if (ts_tst_info) + TS_TST_INFO_free(ts_tst_info); + return 0; + } - if (digestLen != mdLen || memcmp(calc_digest, digestData, mdLen) != 0) - { - result->verify_flags = COUNTERSIGNATURE_VFY_DOESNT_MATCH_SIGNATURE; - goto end; - } + TS_MSG_IMPRINT* ts_imprint = TS_TST_INFO_get_msg_imprint(ts_tst_info); + if (!ts_imprint) { + TS_TST_INFO_free(ts_tst_info); + return 0; + } + + ASN1_OCTET_STRING* ts_imprint_digest = TS_MSG_IMPRINT_get_msg(ts_imprint); + if (!ts_imprint_digest) { + TS_TST_INFO_free(ts_tst_info); + return 0; + } + + if (ts_imprint_digest->length != (int)digest_size || + memcmp(ts_imprint_digest->data, digest, digest_size) != 0) { + TS_TST_INFO_free(ts_tst_info); + return 0; + } + + TS_TST_INFO_free(ts_tst_info); + return 1; +} + +BIO* IMPL_FUNC_NAME(verify_signature_init, pkcs7)(CountersignatureImpl* impl) +{ + assert(impl->type == CS_IMPL_PKCS7); + + return PKCS7_dataInit(impl->pkcs7, NULL); +} + +BIO* IMPL_FUNC_NAME(verify_signature_init, cms)(CountersignatureImpl* impl) +{ + assert(impl->type == CS_IMPL_CMS); + + return CMS_dataInit(impl->cms, NULL); +} + +int IMPL_FUNC_NAME(verify_signature_finish, pkcs7)( + CountersignatureImpl* impl, BIO* bio, X509* signer) +{ + assert(impl->type == CS_IMPL_PKCS7); + + /* Verify signature with PKCS7_signatureVerify + because TS_RESP_verify_token would try to verify + chain and without trust anchors it always fails */ + PKCS7_SIGNER_INFO* si = sk_PKCS7_SIGNER_INFO_value(PKCS7_get_signer_info(impl->pkcs7), 0); + return PKCS7_signatureVerify(bio, impl->pkcs7, si, signer); +} + +int IMPL_FUNC_NAME(verify_signature_finish, cms)(CountersignatureImpl* impl, BIO* bio, X509* signer) +{ + assert(impl->type == CS_IMPL_CMS); + + (void)signer; + CMS_SignerInfo* si = sk_CMS_SignerInfo_value(CMS_get0_SignerInfos(impl->cms), 0); + return CMS_SignerInfo_verify_content(si, bio); +} + +CountersignatureImpl* ms_countersig_impl_new(const uint8_t* data, long size) +{ + const uint8_t* d = data; + PKCS7* p7 = d2i_PKCS7(NULL, &d, size); + if (p7 && PKCS7_type_is_signed(p7) && p7->d.sign) { + CountersignatureImpl* result = + (CountersignatureImpl*)calloc(1, sizeof(CountersignatureImpl)); + result->type = CS_IMPL_PKCS7; + result->funcs = &FUNC_ARRAY_NAME_FOR_IMPL(pkcs7); + result->pkcs7 = p7; + return result; + } + + d = data; + CMS_ContentInfo* cms = d2i_CMS_ContentInfo(NULL, &d, size); + if (cms) { + CountersignatureImpl* result = + (CountersignatureImpl*)calloc(1, sizeof(CountersignatureImpl)); + result->type = CS_IMPL_CMS; + result->funcs = &FUNC_ARRAY_NAME_FOR_IMPL(cms); + result->cms = cms; + return result; + } + + return NULL; +} + +void ms_countersig_impl_free(CountersignatureImpl* impl) +{ + switch (impl->type) { + case CS_IMPL_PKCS7: + PKCS7_free(impl->pkcs7); + break; + case CS_IMPL_CMS: + if (impl->_certs) { + sk_X509_pop_free(impl->_certs, X509_free); + } + CMS_ContentInfo_free(impl->cms); + break; + } + + free(impl); +} + +Countersignature* ms_countersig_new(const uint8_t* data, long size, ASN1_STRING* enc_digest) +{ + Countersignature* result = (Countersignature*)calloc(1, sizeof(*result)); + if (!result) + return NULL; + + CountersignatureImpl* impl = ms_countersig_impl_new(data, size); + if (!impl) { + result->verify_flags = COUNTERSIGNATURE_VFY_CANT_PARSE; + return result; + } + + TS_TST_INFO* ts = impl->funcs->get_ts_tst_info(impl); + if (!ts) { + result->verify_flags = COUNTERSIGNATURE_VFY_CANT_PARSE; + ms_countersig_impl_free(impl); + return result; + } + + const ASN1_TIME* rawTime = TS_TST_INFO_get_time(ts); + if (!rawTime) { + result->verify_flags = COUNTERSIGNATURE_VFY_TIME_MISSING; + TS_TST_INFO_free(ts); + ms_countersig_impl_free(impl); + return result; + } + + result->sign_time = ASN1_TIME_to_int64_t(rawTime); + + STACK_OF(X509)* sigs = impl->funcs->get_signers(impl); + X509* signCert = sk_X509_value(sigs, 0); + if (!signCert) { + result->verify_flags = COUNTERSIGNATURE_VFY_NO_SIGNER_CERT; + goto end; + } + + STACK_OF(X509)* certs = impl->funcs->get_certs(impl); + result->chain = parse_signer_chain(signCert, certs); + + /* Imprint == digest */ + TS_MSG_IMPRINT* imprint = TS_TST_INFO_get_msg_imprint(ts); + if (!imprint) { + result->verify_flags = COUNTERSIGNATURE_VFY_DIGEST_MISSING; + goto end; + } + + X509_ALGOR* digestAlg = TS_MSG_IMPRINT_get_algo(imprint); + int digestnid = OBJ_obj2nid(digestAlg->algorithm); + result->digest_alg = strdup(OBJ_nid2ln(digestnid)); - TS_VERIFY_CTX* ctx = TS_VERIFY_CTX_new(); - X509_STORE* store = X509_STORE_new(); - TS_VERIFY_CTX_init(ctx); + ASN1_STRING* rawDigest = TS_MSG_IMPRINT_get_msg(imprint); + + int digestLen = rawDigest->length; + uint8_t* digestData = rawDigest->data; + + byte_array_init(&result->digest, digestData, digestLen); + + if (!digestLen) { + result->verify_flags = COUNTERSIGNATURE_VFY_DIGEST_MISSING; + goto end; + } + + const EVP_MD* md = EVP_get_digestbynid(digestnid); + if (!md) { + result->verify_flags = COUNTERSIGNATURE_VFY_UNKNOWN_ALGORITHM; + goto end; + } + + uint8_t calc_digest[EVP_MAX_MD_SIZE]; + calculate_digest(md, enc_digest->data, enc_digest->length, calc_digest); - TS_VERIFY_CTX_set_flags(ctx, TS_VFY_VERSION | TS_VFY_IMPRINT); - TS_VERIFY_CTX_set_store(ctx, store); #if OPENSSL_VERSION_NUMBER >= 0x3000000fL - TS_VERIFY_CTX_set_certs(ctx, p7->d.sign->cert); + int mdLen = EVP_MD_get_size(md); #else - TS_VERIFY_CTS_set_certs(ctx, p7->d.sign->cert); + int mdLen = EVP_MD_size(md); #endif - TS_VERIFY_CTX_set_imprint(ctx, calc_digest, mdLen); - - bool isValid = TS_RESP_verify_token(ctx, p7) == 1; - - X509_STORE_free(store); - OPENSSL_free(ctx); - if (!isValid) - { - result->verify_flags = COUNTERSIGNATURE_VFY_INVALID; - goto end; - } + if (digestLen != mdLen || memcmp(calc_digest, digestData, mdLen) != 0) { + result->verify_flags = COUNTERSIGNATURE_VFY_DOESNT_MATCH_SIGNATURE; + goto end; + } - /* Verify signature with PKCS7_signatureVerify - because TS_RESP_verify_token would try to verify - chain and without trust anchors it always fails */ - BIO* p7bio = PKCS7_dataInit(p7, NULL); + bool isValid = impl->funcs->verify_digest(impl, calc_digest, mdLen) == 1; + if (!isValid) { + result->verify_flags = COUNTERSIGNATURE_VFY_INVALID; + goto end; + } - char buf[4096]; - /* We now have to 'read' from p7bio to calculate digests etc. */ - while (BIO_read(p7bio, buf, sizeof(buf)) > 0) continue; + BIO* bio = impl->funcs->verify_signature_init(impl); - PKCS7_SIGNER_INFO* si = sk_PKCS7_SIGNER_INFO_value( - PKCS7_get_signer_info(p7), 0); + char buf[4096]; + /* We now have to 'read' from bio to calculate digests etc. */ + while (BIO_read(bio, buf, sizeof(buf)) > 0) + continue; - isValid = PKCS7_signatureVerify(p7bio, p7, si, signCert) == 1; + isValid = impl->funcs->verify_signature_finish(impl, bio, signCert) == 1; - BIO_free_all(p7bio); + BIO_free_all(bio); - if (!isValid) - result->verify_flags = COUNTERSIGNATURE_VFY_INVALID; + if (!isValid) + result->verify_flags = COUNTERSIGNATURE_VFY_INVALID; end: - sk_X509_free(sigs); - PKCS7_free(p7); - TS_TST_INFO_free(ts); - return result; + sk_X509_free(sigs); + TS_TST_INFO_free(ts); + ms_countersig_impl_free(impl); + return result; } -int countersignature_array_insert( - CountersignatureArray* arr, - Countersignature* sig) +int countersignature_array_insert(CountersignatureArray* arr, Countersignature* sig) { - Countersignature** tmp = (Countersignature**) realloc( - arr->counters, (arr->count + 1) * sizeof(Countersignature*)); - if (!tmp) - return 1; + Countersignature** tmp = + (Countersignature**)realloc(arr->counters, (arr->count + 1) * sizeof(Countersignature*)); + if (!tmp) + return 1; - arr->counters = tmp; - arr->counters[arr->count] = sig; - arr->count++; + arr->counters = tmp; + arr->counters[arr->count] = sig; + arr->count++; - return 0; + return 0; } -int countersignature_array_move( - CountersignatureArray* dst, - CountersignatureArray* src) +int countersignature_array_move(CountersignatureArray* dst, CountersignatureArray* src) { - size_t newCount = dst->count + src->count; + size_t newCount = dst->count + src->count; - Countersignature** tmp = (Countersignature**) realloc( - dst->counters, newCount * sizeof(Countersignature*)); - if (!tmp) - return 1; + Countersignature** tmp = + (Countersignature**)realloc(dst->counters, newCount * sizeof(Countersignature*)); + if (!tmp) + return 1; - dst->counters = tmp; + dst->counters = tmp; - for (size_t i = 0; i < src->count; ++i) - dst->counters[i + dst->count] = src->counters[i]; + for (size_t i = 0; i < src->count; ++i) + dst->counters[i + dst->count] = src->counters[i]; - dst->count = newCount; + dst->count = newCount; - free(src->counters); - src->counters = NULL; - src->count = 0; + free(src->counters); + src->counters = NULL; + src->count = 0; - return 0; + return 0; } void countersignature_free(Countersignature* sig) { - if (sig) - { - free(sig->digest_alg); - free(sig->digest.data); - certificate_array_free(sig->chain); - free(sig); - } + if (sig) { + free(sig->digest_alg); + free(sig->digest.data); + certificate_array_free(sig->chain); + free(sig); + } } void countersignature_array_free(CountersignatureArray* arr) { - if (arr) - { - for (size_t i = 0; i < arr->count; ++i) - { - countersignature_free(arr->counters[i]); - } - free(arr->counters); - free(arr); - } + if (arr) { + for (size_t i = 0; i < arr->count; ++i) { + countersignature_free(arr->counters[i]); + } + free(arr->counters); + free(arr); + } } diff --git a/libyara/modules/pe/authenticode-parser/countersignature.h b/libyara/modules/pe/authenticode-parser/countersignature.h index b14012cc32..294ffed738 100644 --- a/libyara/modules/pe/authenticode-parser/countersignature.h +++ b/libyara/modules/pe/authenticode-parser/countersignature.h @@ -22,40 +22,29 @@ SOFTWARE. #ifndef AUTHENTICODE_PARSER_COUNTERSIGNATURE_H #define AUTHENTICODE_PARSER_COUNTERSIGNATURE_H +#include "certificate.h" +#include "helper.h" #include #include #include -#include "certificate.h" -#include "helper.h" #include #include #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif - Countersignature* pkcs9_countersig_new( - const uint8_t* data, - long size, - STACK_OF(X509) * certs, - ASN1_STRING* enc_digest); - Countersignature* ms_countersig_new( - const uint8_t* data, - long size, - ASN1_STRING* enc_digest); +Countersignature* pkcs9_countersig_new( + const uint8_t* data, long size, STACK_OF(X509) * certs, ASN1_STRING* enc_digest); +Countersignature* ms_countersig_new(const uint8_t* data, long size, ASN1_STRING* enc_digest); - int countersignature_array_insert( - CountersignatureArray* arr, - Countersignature* sig); - /* Moves all countersignatures of src and inserts them into dst */ - int countersignature_array_move( - CountersignatureArray* dst, - CountersignatureArray* src); +int countersignature_array_insert(CountersignatureArray* arr, Countersignature* sig); +/* Moves all countersignatures of src and inserts them into dst */ +int countersignature_array_move(CountersignatureArray* dst, CountersignatureArray* src); - void countersignature_free(Countersignature* sig); - void countersignature_array_free(CountersignatureArray* arr); +void countersignature_free(Countersignature* sig); +void countersignature_array_free(CountersignatureArray* arr); #ifdef __cplusplus } diff --git a/libyara/modules/pe/authenticode-parser/helper.c b/libyara/modules/pe/authenticode-parser/helper.c index 6845c64d50..ecdf7ca84e 100644 --- a/libyara/modules/pe/authenticode-parser/helper.c +++ b/libyara/modules/pe/authenticode-parser/helper.c @@ -30,61 +30,55 @@ SOFTWARE. uint16_t bswap16(uint16_t d) { - return (d << 8) | (d >> 8); + return (d << 8) | (d >> 8); } uint32_t bswap32(uint32_t d) { - return (((d) &0xff000000) >> 24) | (((d) &0x00ff0000) >> 8) | - (((d) &0x0000ff00) << 8) | (((d) &0x000000ff) << 24); + return (((d)&0xff000000) >> 24) | (((d)&0x00ff0000) >> 8) | (((d)&0x0000ff00) << 8) | + (((d)&0x000000ff) << 24); } -int calculate_digest( - const EVP_MD* md, - const uint8_t* data, - size_t len, - uint8_t* digest) +int calculate_digest(const EVP_MD* md, const uint8_t* data, size_t len, uint8_t* digest) { - unsigned int outLen = 0; + unsigned int outLen = 0; - EVP_MD_CTX* mdCtx = EVP_MD_CTX_new(); - if (!mdCtx) - goto end; + EVP_MD_CTX* mdCtx = EVP_MD_CTX_new(); + if (!mdCtx) + goto end; - if (!EVP_DigestInit_ex(mdCtx, md, NULL) || - !EVP_DigestUpdate(mdCtx, data, len) || - !EVP_DigestFinal_ex(mdCtx, digest, &outLen)) - goto end; + if (!EVP_DigestInit_ex(mdCtx, md, NULL) || !EVP_DigestUpdate(mdCtx, data, len) || + !EVP_DigestFinal_ex(mdCtx, digest, &outLen)) + goto end; end: - EVP_MD_CTX_free(mdCtx); - return (int) outLen; + EVP_MD_CTX_free(mdCtx); + return (int)outLen; } int byte_array_init(ByteArray* arr, const uint8_t* data, int len) { - if (len == 0) - { - arr->data = NULL; - arr->len = 0; + if (len == 0) { + arr->data = NULL; + arr->len = 0; + return 0; + } + + arr->data = (uint8_t*)malloc(len); + if (!arr->data) + return -1; + + arr->len = len; + memcpy(arr->data, data, len); return 0; - } - - arr->data = (uint8_t*) malloc(len); - if (!arr->data) - return -1; - - arr->len = len; - memcpy(arr->data, data, len); - return 0; } -time_t ASN1_TIME_to_time_t(const ASN1_TIME* time) +int64_t ASN1_TIME_to_int64_t(const ASN1_TIME* time) { - struct tm t = {0}; - if (!time) - return timegm(&t); + struct tm t = {0}; + if (!time) + return timegm(&t); - ASN1_TIME_to_tm(time, &t); - return timegm(&t); + ASN1_TIME_to_tm(time, &t); + return timegm(&t); } diff --git a/libyara/modules/pe/authenticode-parser/helper.h b/libyara/modules/pe/authenticode-parser/helper.h index 130047a02e..dc1261df1b 100644 --- a/libyara/modules/pe/authenticode-parser/helper.h +++ b/libyara/modules/pe/authenticode-parser/helper.h @@ -34,13 +34,12 @@ SOFTWARE. #endif #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif - /* Endianity related functions for PE reading */ - uint16_t bswap16(uint16_t d); - uint32_t bswap32(uint32_t d); +/* Endianity related functions for PE reading */ +uint16_t bswap16(uint16_t d); +uint32_t bswap32(uint32_t d); #if defined(WORDS_BIGENDIAN) #define letoh16(x) bswap16(x) @@ -54,17 +53,13 @@ extern "C" #define betoh32(x) bswap32(x) #endif - /* Calculates digest md of data, return bytes written to digest or 0 on error - * Maximum of EVP_MAX_MD_SIZE will be written to digest */ - int calculate_digest( - const EVP_MD* md, - const uint8_t* data, - size_t len, - uint8_t* digest); - /* Copies data of length len into already existing arr */ - int byte_array_init(ByteArray* arr, const uint8_t* data, int len); - /* Converts ASN1_TIME string time into a unix timestamp */ - time_t ASN1_TIME_to_time_t(const ASN1_TIME* time); +/* Calculates digest md of data, return bytes written to digest or 0 on error + * Maximum of EVP_MAX_MD_SIZE will be written to digest */ +int calculate_digest(const EVP_MD* md, const uint8_t* data, size_t len, uint8_t* digest); +/* Copies data of length len into already existing arr */ +int byte_array_init(ByteArray* arr, const uint8_t* data, int len); +/* Converts ASN1_TIME string time into a unix timestamp */ +int64_t ASN1_TIME_to_int64_t(const ASN1_TIME* time); #ifdef __cplusplus } diff --git a/libyara/modules/pe/authenticode-parser/structs.c b/libyara/modules/pe/authenticode-parser/structs.c index e9fcfa2bcf..4eee46b4d7 100644 --- a/libyara/modules/pe/authenticode-parser/structs.c +++ b/libyara/modules/pe/authenticode-parser/structs.c @@ -22,68 +22,50 @@ SOFTWARE. #include "structs.h" ASN1_CHOICE(SpcString) = { - ASN1_IMP_OPT(SpcString, value.unicode, ASN1_BMPSTRING, 0), - ASN1_IMP_OPT( - SpcString, - value.ascii, - ASN1_IA5STRING, - 1)} ASN1_CHOICE_END(SpcString); + ASN1_IMP_OPT(SpcString, value.unicode, ASN1_BMPSTRING, 0), + ASN1_IMP_OPT(SpcString, value.ascii, ASN1_IA5STRING, 1) +} ASN1_CHOICE_END(SpcString) ASN1_SEQUENCE(SpcSerializedObject) = { - ASN1_SIMPLE(SpcSerializedObject, classId, ASN1_OCTET_STRING), - ASN1_SIMPLE( - SpcSerializedObject, - serializedData, - ASN1_OCTET_STRING)} ASN1_SEQUENCE_END(SpcSerializedObject); + ASN1_SIMPLE(SpcSerializedObject, classId, ASN1_OCTET_STRING), + ASN1_SIMPLE(SpcSerializedObject, serializedData, ASN1_OCTET_STRING) +} ASN1_SEQUENCE_END(SpcSerializedObject) ASN1_CHOICE(SpcLink) = { - ASN1_IMP_OPT(SpcLink, value.url, ASN1_IA5STRING, 0), - ASN1_IMP_OPT(SpcLink, value.moniker, SpcSerializedObject, 1), - ASN1_EXP_OPT(SpcLink, value.file, SpcString, 2)} ASN1_CHOICE_END(SpcLink); + ASN1_IMP_OPT(SpcLink, value.url, ASN1_IA5STRING, 0), + ASN1_IMP_OPT(SpcLink, value.moniker, SpcSerializedObject, 1), + ASN1_EXP_OPT(SpcLink, value.file, SpcString, 2) +} ASN1_CHOICE_END(SpcLink) ASN1_SEQUENCE(SpcAttributeTypeAndOptionalValue) = { - ASN1_SIMPLE(SpcAttributeTypeAndOptionalValue, type, ASN1_OBJECT), - ASN1_OPT( - SpcAttributeTypeAndOptionalValue, - value, - ASN1_ANY)} ASN1_SEQUENCE_END(SpcAttributeTypeAndOptionalValue); + ASN1_SIMPLE(SpcAttributeTypeAndOptionalValue, type, ASN1_OBJECT), + ASN1_OPT(SpcAttributeTypeAndOptionalValue, value, ASN1_ANY) +} ASN1_SEQUENCE_END(SpcAttributeTypeAndOptionalValue) ASN1_SEQUENCE(SpcPeImageData) = { - ASN1_SIMPLE(SpcPeImageData, flags, ASN1_BIT_STRING), - ASN1_EXP_OPT( - SpcPeImageData, - file, - SpcLink, - 0)} ASN1_SEQUENCE_END(SpcPeImageData); + ASN1_SIMPLE(SpcPeImageData, flags, ASN1_BIT_STRING), + ASN1_EXP_OPT(SpcPeImageData, file, SpcLink, 0) +} ASN1_SEQUENCE_END(SpcPeImageData) ASN1_SEQUENCE(AlgorithmIdentifier) = { - ASN1_SIMPLE(AlgorithmIdentifier, algorithm, ASN1_OBJECT), - ASN1_OPT( - AlgorithmIdentifier, - parameters, - ASN1_ANY)} ASN1_SEQUENCE_END(AlgorithmIdentifier); + ASN1_SIMPLE(AlgorithmIdentifier, algorithm, ASN1_OBJECT), + ASN1_OPT(AlgorithmIdentifier, parameters, ASN1_ANY) +} ASN1_SEQUENCE_END(AlgorithmIdentifier) ASN1_SEQUENCE(DigestInfo) = { - ASN1_SIMPLE(DigestInfo, digestAlgorithm, AlgorithmIdentifier), - ASN1_SIMPLE( - DigestInfo, - digest, - ASN1_OCTET_STRING)} ASN1_SEQUENCE_END(DigestInfo); + ASN1_SIMPLE(DigestInfo, digestAlgorithm, AlgorithmIdentifier), + ASN1_SIMPLE(DigestInfo, digest, ASN1_OCTET_STRING) +} ASN1_SEQUENCE_END(DigestInfo) ASN1_SEQUENCE(SpcIndirectDataContent) = { - ASN1_SIMPLE(SpcIndirectDataContent, data, SpcAttributeTypeAndOptionalValue), - ASN1_SIMPLE( - SpcIndirectDataContent, - messageDigest, - DigestInfo)} ASN1_SEQUENCE_END(SpcIndirectDataContent); + ASN1_SIMPLE(SpcIndirectDataContent, data, SpcAttributeTypeAndOptionalValue), + ASN1_SIMPLE(SpcIndirectDataContent, messageDigest, DigestInfo) +} ASN1_SEQUENCE_END(SpcIndirectDataContent) ASN1_SEQUENCE(SpcSpOpusInfo) = { - ASN1_EXP_OPT(SpcSpOpusInfo, programName, SpcString, 0), - ASN1_EXP_OPT( - SpcSpOpusInfo, - moreInfo, - SpcLink, - 1)} ASN1_SEQUENCE_END(SpcSpOpusInfo); + ASN1_EXP_OPT(SpcSpOpusInfo, programName, SpcString, 0), + ASN1_EXP_OPT(SpcSpOpusInfo, moreInfo, SpcLink, 1) +} ASN1_SEQUENCE_END(SpcSpOpusInfo) IMPLEMENT_ASN1_FUNCTIONS(SpcString) IMPLEMENT_ASN1_FUNCTIONS(SpcSerializedObject) diff --git a/libyara/modules/pe/authenticode-parser/structs.h b/libyara/modules/pe/authenticode-parser/structs.h index 7a38d9b42e..1f90db69b8 100644 --- a/libyara/modules/pe/authenticode-parser/structs.h +++ b/libyara/modules/pe/authenticode-parser/structs.h @@ -28,8 +28,7 @@ SOFTWARE. #include #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif #define NID_spc_info "1.3.6.1.4.1.311.2.1.12" @@ -37,85 +36,73 @@ extern "C" #define NID_spc_nested_signature "1.3.6.1.4.1.311.2.4.1" #define NID_spc_indirect_data "1.3.6.1.4.1.311.2.1.4" - typedef struct - { +typedef struct { int type; - union - { - ASN1_BMPSTRING *unicode; - ASN1_IA5STRING *ascii; + union { + ASN1_BMPSTRING *unicode; + ASN1_IA5STRING *ascii; } value; - } SpcString; +} SpcString; - typedef struct - { +typedef struct { ASN1_OCTET_STRING *classId; ASN1_OCTET_STRING *serializedData; - } SpcSerializedObject; +} SpcSerializedObject; - typedef struct - { +typedef struct { int type; - union - { - ASN1_IA5STRING *url; - SpcSerializedObject *moniker; - SpcString *file; + union { + ASN1_IA5STRING *url; + SpcSerializedObject *moniker; + SpcString *file; } value; - } SpcLink; +} SpcLink; - typedef struct - { +typedef struct { ASN1_OBJECT *type; ASN1_TYPE *value; - } SpcAttributeTypeAndOptionalValue; +} SpcAttributeTypeAndOptionalValue; - typedef struct - { +typedef struct { ASN1_BIT_STRING *flags; SpcLink *file; - } SpcPeImageData; +} SpcPeImageData; - typedef struct - { +typedef struct { ASN1_OBJECT *algorithm; ASN1_TYPE *parameters; - } AlgorithmIdentifier; +} AlgorithmIdentifier; - typedef struct - { +typedef struct { AlgorithmIdentifier *digestAlgorithm; ASN1_OCTET_STRING *digest; - } DigestInfo; +} DigestInfo; - typedef struct - { +typedef struct { SpcAttributeTypeAndOptionalValue *data; DigestInfo *messageDigest; - } SpcIndirectDataContent; +} SpcIndirectDataContent; - typedef struct - { +typedef struct { ASN1_OBJECT *contentType; SpcIndirectDataContent *content; - } SpcContentInfo; +} SpcContentInfo; - typedef struct - { +typedef struct { SpcString *programName; SpcLink *moreInfo; - } SpcSpOpusInfo; - - DECLARE_ASN1_FUNCTIONS(SpcString) - DECLARE_ASN1_FUNCTIONS(SpcSerializedObject) - DECLARE_ASN1_FUNCTIONS(SpcLink) - DECLARE_ASN1_FUNCTIONS(SpcAttributeTypeAndOptionalValue) - DECLARE_ASN1_FUNCTIONS(SpcPeImageData) - DECLARE_ASN1_FUNCTIONS(AlgorithmIdentifier) - DECLARE_ASN1_FUNCTIONS(DigestInfo) - DECLARE_ASN1_FUNCTIONS(SpcIndirectDataContent) - DECLARE_ASN1_FUNCTIONS(SpcSpOpusInfo) - DECLARE_ASN1_FUNCTIONS(SpcContentInfo) +} SpcSpOpusInfo; + +DECLARE_ASN1_FUNCTIONS(SpcString) +DECLARE_ASN1_FUNCTIONS(SpcSerializedObject) +DECLARE_ASN1_FUNCTIONS(SpcLink) +DECLARE_ASN1_FUNCTIONS(SpcAttributeTypeAndOptionalValue) +DECLARE_ASN1_FUNCTIONS(SpcPeImageData) +DECLARE_ASN1_FUNCTIONS(AlgorithmIdentifier) +DECLARE_ASN1_FUNCTIONS(DigestInfo) +DECLARE_ASN1_FUNCTIONS(SpcIndirectDataContent) +DECLARE_ASN1_FUNCTIONS(SpcSpOpusInfo) +DECLARE_ASN1_FUNCTIONS(SpcContentInfo) #ifdef __cplusplus }