diff --git a/CMakeLists.txt b/CMakeLists.txt index fa2be03..470991f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,14 +97,25 @@ set_target_properties(osslsigncode PROPERTIES INSTALL_RPATH_USE_LINK_PATH TRUE) # testing with CTest include(CMakeTest) +# documentation with Pandoc +include(CMakeDoc) + # installation rules for a project -set(BINDIR "${CMAKE_INSTALL_PREFIX}/bin") -install(TARGETS osslsigncode RUNTIME DESTINATION ${BINDIR}) +include(GNUInstallDirs) + +install(TARGETS osslsigncode RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +install(FILES + "${PROJECT_SOURCE_DIR}/README.md" + "${PROJECT_SOURCE_DIR}/NEWS.md" + DESTINATION "${CMAKE_INSTALL_DOCDIR}") + if(UNIX) include(CMakeDist) else(UNIX) install( - DIRECTORY ${PROJECT_BINARY_DIR}/ DESTINATION ${BINDIR} + DIRECTORY ${PROJECT_BINARY_DIR}/ + DESTINATION ${CMAKE_INSTALL_BINDIR} FILES_MATCHING PATTERN "*.dll" PATTERN "vcpkg_installed" EXCLUDE @@ -112,6 +123,15 @@ else(UNIX) PATTERN "Testing" EXCLUDE) endif(UNIX) +# uninstall target +configure_file( + "${PROJECT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" + "${PROJECT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY) + +add_custom_target(uninstall + COMMAND ${CMAKE_COMMAND} -P "${PROJECT_BINARY_DIR}/cmake_uninstall.cmake") + #[[ Local Variables: c-basic-offset: 4 diff --git a/appx.c b/appx.c index 6728f7d..4544068 100644 --- a/appx.c +++ b/appx.c @@ -470,32 +470,33 @@ static int appx_hash_length_get(FILE_FORMAT_CTX *ctx) */ static int appx_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7) { - if (is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) { - ASN1_STRING *content_val = p7->d.sign->contents->d.other->value.sequence; - const u_char *p = content_val->data; - SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length); - - if (idc) { - BIO *hashes; - if (!appx_extract_hashes(ctx, idc)) { - fprintf(stderr, "Failed to extract hashes from the signature\n"); - SpcIndirectDataContent_free(idc); - return 0; /* FAILED */ - } - hashes = appx_calculate_hashes(ctx); - if (!hashes) { - SpcIndirectDataContent_free(idc); - return 0; /* FAILED */ - } - BIO_free_all(hashes); - if (!appx_compare_hashes(ctx)) { - fprintf(stderr, "Signature hash verification failed\n"); - SpcIndirectDataContent_free(idc); - return 0; /* FAILED */ - } - SpcIndirectDataContent_free(idc); - } + SpcIndirectDataContent *idc; + BIO *hashes; + + idc = pkcs7_get_indirect_data_content(p7); + if (!idc) + return 1; /* OK - no SpcIndirectDataContent */ + + if (!appx_extract_hashes(ctx, idc)) { + fprintf(stderr, "Failed to extract hashes from the signature\n"); + SpcIndirectDataContent_free(idc); + return 0; /* FAILED */ } + + hashes = appx_calculate_hashes(ctx); + if (!hashes) { + SpcIndirectDataContent_free(idc); + return 0; /* FAILED */ + } + BIO_free_all(hashes); + + if (!appx_compare_hashes(ctx)) { + fprintf(stderr, "Signature hash verification failed\n"); + SpcIndirectDataContent_free(idc); + return 0; /* FAILED */ + } + + SpcIndirectDataContent_free(idc); return 1; /* OK */ } @@ -1077,13 +1078,13 @@ static int appx_extract_hashes(FILE_FORMAT_CTX *ctx, SpcIndirectDataContent *con AppxSpcSipInfo_free(si); BIO_free_all(stdbio); #endif - int length = content->messageDigest->digest->length; - uint8_t *data = content->messageDigest->digest->data; + int len = ASN1_STRING_length(content->messageDigest->digest); + const uint8_t *data = ASN1_STRING_get0_data(content->messageDigest->digest); int mdlen = EVP_MD_size(ctx->appx_ctx->md); int pos = 4; /* we are expecting at least 4 hashes + 4 byte header */ - if (length < 4 * mdlen + 4) { + if (len < 4 * mdlen + 4) { fprintf(stderr, "Hash too short\n"); return 0; /* FAILED */ } @@ -1091,7 +1092,7 @@ static int appx_extract_hashes(FILE_FORMAT_CTX *ctx, SpcIndirectDataContent *con fprintf(stderr, "Hash signature does not match\n"); return 0; /* FAILED */ } - while (pos + mdlen + 4 <= length) { + while (pos + mdlen + 4 <= len) { if (!memcmp(data + pos, AXPC_SIGNATURE, 4)) { ctx->appx_ctx->existingDataHash = OPENSSL_malloc((size_t)mdlen); memcpy(ctx->appx_ctx->existingDataHash, data + pos + 4, (size_t)mdlen); diff --git a/cab.c b/cab.c index 257c2f0..b9337f5 100644 --- a/cab.c +++ b/cab.c @@ -337,20 +337,7 @@ static int cab_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7) u_char mdbuf[EVP_MAX_MD_SIZE]; u_char *cmdbuf; - if (is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) { - ASN1_STRING *content_val = p7->d.sign->contents->d.other->value.sequence; - const u_char *p = content_val->data; - SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length); - if (idc) { - if (spc_indirect_data_content_get_digest(idc, mdbuf, &mdtype) < 0) { - fprintf(stderr, "Failed to extract message digest from signature\n\n"); - SpcIndirectDataContent_free(idc); - return 0; /* FAILED */ - } - SpcIndirectDataContent_free(idc); - } - } - if (mdtype == -1) { + if (!pkcs7_get_content_digest(p7, mdbuf, &mdtype)) { fprintf(stderr, "Failed to extract current message digest\n\n"); return 0; /* FAILED */ } diff --git a/cat.c b/cat.c index 8967d77..83bf822 100644 --- a/cat.c +++ b/cat.c @@ -297,27 +297,39 @@ static int cat_add_content_type(PKCS7 *p7, PKCS7 *cursig) */ static int cat_sign_content(PKCS7 *p7, PKCS7 *contents) { - u_char *content; - int seqhdrlen, content_length; + const unsigned char *sequence_data; + const unsigned char *content; + ASN1_STRING *sequence; + int seqhdrlen, sequence_len, content_length; - if (!contents->d.other || !contents->d.other->value.sequence - || !contents->d.other->value.sequence->data) { + if (!contents->d.other || !contents->d.other->value.sequence) { fprintf(stderr, "Failed to get content value\n"); return 0; /* FAILED */ } - seqhdrlen = asn1_simple_hdr_len(contents->d.other->value.sequence->data, - contents->d.other->value.sequence->length); - content = contents->d.other->value.sequence->data + seqhdrlen; - content_length = contents->d.other->value.sequence->length - seqhdrlen; + + sequence = contents->d.other->value.sequence; + sequence_data = ASN1_STRING_get0_data(sequence); + sequence_len = ASN1_STRING_length(sequence); + + if (!sequence_data) { + fprintf(stderr, "Failed to get content value\n"); + return 0; /* FAILED */ + } + + seqhdrlen = asn1_simple_hdr_len(sequence_data, sequence_len); + content = (const unsigned char *)sequence_data + seqhdrlen; + content_length = sequence_len - seqhdrlen; if (!pkcs7_sign_content(p7, content, content_length)) { fprintf(stderr, "Failed to sign content\n"); return 0; /* FAILED */ } + if (!PKCS7_set_content(p7, PKCS7_dup(contents))) { fprintf(stderr, "PKCS7_set_content failed\n"); return 0; /* FAILED */ } + return 1; /* OK */ } @@ -381,23 +393,22 @@ static int cat_print_content_member_digest(ASN1_TYPE *content) { SpcIndirectDataContent *idc; u_char mdbuf[EVP_MAX_MD_SIZE]; - const u_char *data ; int mdtype = -1; - ASN1_STRING *value; - value = content->value.sequence; - data = ASN1_STRING_get0_data(value); - idc = d2i_SpcIndirectDataContent(NULL, &data, ASN1_STRING_length(value)); + idc = asn1_type_get_indirect_data_content(content); if (!idc) return 0; /* FAILED */ + if (spc_indirect_data_content_get_digest(idc, mdbuf, &mdtype) < 0) { fprintf(stderr, "Failed to extract message digest from signature\n\n"); SpcIndirectDataContent_free(idc); return 0; /* FAILED */ } SpcIndirectDataContent_free(idc); + printf("\tHash algorithm: %s\n", OBJ_nid2sn(mdtype)); print_hash("\tMessage digest", "", mdbuf, EVP_MD_size(EVP_get_digestbynid(mdtype))); + return 1; /* OK */ } diff --git a/cmake/CMakeDoc.cmake b/cmake/CMakeDoc.cmake new file mode 100644 index 0000000..f66c8a6 --- /dev/null +++ b/cmake/CMakeDoc.cmake @@ -0,0 +1,44 @@ +# documentation with Pandoc +# cmake --build . + +find_program(PANDOC pandoc) + +if(NOT PANDOC) + message(WARNING "CMakeDoc: pandoc not found, documentation disabled") + return() +endif(NOT PANDOC) + +set(DOC_MD "${PROJECT_SOURCE_DIR}/osslsigncode.md") + +if(NOT EXISTS "${DOC_MD}") + message(WARNING "CMakeDoc: markdown source not found: ${DOC_MD}") + return() +endif(NOT EXISTS "${DOC_MD}") + +set(MAN_PAGE "${PROJECT_BINARY_DIR}/osslsigncode.1") +set(HTML_PAGE "${PROJECT_BINARY_DIR}/osslsigncode.html") + +add_custom_command( + OUTPUT "${MAN_PAGE}" + COMMAND "${PANDOC}" -s "${DOC_MD}" -t man -o "${MAN_PAGE}" + DEPENDS "${DOC_MD}" + COMMENT "CMakeDoc: generating man page" + VERBATIM) + +add_custom_command( + OUTPUT "${HTML_PAGE}" + COMMAND "${PANDOC}" -s --toc --toc-depth=2 "${DOC_MD}" -t html -o "${HTML_PAGE}" + DEPENDS "${DOC_MD}" + COMMENT "CMakeDoc: generating HTML documentation" + VERBATIM) + +add_custom_target(docs ALL DEPENDS "${MAN_PAGE}" "${HTML_PAGE}") + +#[[ +Local Variables: + c-basic-offset: 4 + tab-width: 4 + indent-tabs-mode: nil +End: + vim: set ts=4 expandtab: +]] diff --git a/cmake/cmake_uninstall.cmake.in b/cmake/cmake_uninstall.cmake.in new file mode 100644 index 0000000..25eb6e7 --- /dev/null +++ b/cmake/cmake_uninstall.cmake.in @@ -0,0 +1,24 @@ +# uninstall target +# +# CMake does not provide a built-in uninstall target. +# This target removes files listed in install_manifest.txt, +# generated by the install step. +# +# cmake --build . --target uninstall + +if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + message(FATAL_ERROR "Cannot find install manifest") +endif() + +file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) +string(REPLACE "\n" ";" files "${files}") + +foreach(file ${files}) + message(STATUS "Removing ${file}") + + if(EXISTS "${file}" OR IS_SYMLINK "${file}") + file(REMOVE "${file}") + else() + message(STATUS "File does not exist: ${file}") + endif() +endforeach() diff --git a/helpers.c b/helpers.c index cbcbc38..ea44d60 100644 --- a/helpers.c +++ b/helpers.c @@ -341,6 +341,91 @@ PKCS7 *pkcs7_set_content(ASN1_OCTET_STRING *content) return p7; } +/* + * Retrieve the message digest and digest algorithm from PKCS7 + * SpcIndirectDataContent. + * + * [in] p7: PKCS7 structure containing SPC_INDIRECT_DATA_OBJID content + * [out] mdbuf: message digest buffer, at least EVP_MAX_MD_SIZE bytes + * [out] mdtype: OpenSSL NID of the digest algorithm + * [returns] 0 on error or 1 on success + */ +int pkcs7_get_content_digest(PKCS7 *p7, u_char *mdbuf, int *mdtype) +{ + SpcIndirectDataContent *idc; + + if (!mdbuf || !mdtype) + return 0; /* FAILED */ + + *mdtype = -1; + + idc = pkcs7_get_indirect_data_content(p7); + if (!idc) { + fprintf(stderr, "Failed to decode SpcIndirectDataContent\n\n"); + return 0; /* FAILED */ + } + if (spc_indirect_data_content_get_digest(idc, mdbuf, mdtype) < 0) { + fprintf(stderr, "Failed to extract message digest from signature\n\n"); + SpcIndirectDataContent_free(idc); + return 0; /* FAILED */ + } + SpcIndirectDataContent_free(idc); + if (*mdtype == -1) { + fprintf(stderr, "Failed to extract current message digest\n\n"); + return 0; /* FAILED */ + } + return 1; /* OK */ +} + +/* + * Decode SpcIndirectDataContent from a PKCS7 signedData content. + * + * [in] p7: PKCS7 structure containing SPC_INDIRECT_DATA_OBJID content + * [returns] newly allocated SpcIndirectDataContent, or NULL on error + * + * The caller is responsible for freeing the returned object with + * SpcIndirectDataContent_free(). + */ +SpcIndirectDataContent *pkcs7_get_indirect_data_content(PKCS7 *p7) +{ + if (!is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) + return NULL; + + if (!p7->d.sign || !p7->d.sign->contents || !p7->d.sign->contents->d.other) + return NULL; + + return asn1_type_get_indirect_data_content(p7->d.sign->contents->d.other); +} + +/* + * Decode SpcIndirectDataContent from an ASN1_TYPE object. + * The ASN1_TYPE is expected to contain a V_ASN1_SEQUENCE value. + * + * [in] content: ASN1_TYPE containing DER-encoded SpcIndirectDataContent + * [returns] newly allocated SpcIndirectDataContent, or NULL on error + * + * The caller is responsible for freeing the returned object with + * SpcIndirectDataContent_free(). + */ +SpcIndirectDataContent *asn1_type_get_indirect_data_content(ASN1_TYPE *content) +{ + ASN1_STRING *value; + const unsigned char *data; + int len; + + if (!content || content->type != V_ASN1_SEQUENCE) + return NULL; + + value = content->value.sequence; + if (!value) + return NULL; + + data = ASN1_STRING_get0_data(value); + len = ASN1_STRING_length(value); + + return d2i_SpcIndirectDataContent(NULL, &data, len); +} + /* * Return spcIndirectDataContent. * [in] hash: message digest BIO @@ -575,22 +660,27 @@ int compare_digests(u_char *mdbuf, u_char *cmdbuf, int mdtype) */ int spc_indirect_data_content_get_digest(SpcIndirectDataContent *idc, u_char *mdbuf, int *mdtype) { + ASN1_OCTET_STRING *digest_asn1; + const unsigned char *digest_data; int digest_len; if (!idc || !idc->messageDigest || !idc->messageDigest->digest || !idc->messageDigest->digestAlgorithm) { return -1; /* FAILED */ } - digest_len = idc->messageDigest->digest->length; + digest_asn1 = idc->messageDigest->digest; + digest_len = ASN1_STRING_length((ASN1_STRING *)digest_asn1); /* Validate digest length to prevent buffer overflow */ if (digest_len <= 0 || digest_len > EVP_MAX_MD_SIZE) { fprintf(stderr, "Invalid digest length in signature: %d (expected 1-%d)\n", - digest_len, EVP_MAX_MD_SIZE); + digest_len, EVP_MAX_MD_SIZE); return -1; /* FAILED */ } + + digest_data = ASN1_STRING_get0_data((ASN1_STRING *)digest_asn1); *mdtype = OBJ_obj2nid(idc->messageDigest->digestAlgorithm->algorithm); - memcpy(mdbuf, idc->messageDigest->digest->data, (size_t)digest_len); + memcpy(mdbuf, digest_data, (size_t)digest_len); return digest_len; /* OK */ } @@ -653,8 +743,12 @@ static int spc_indirect_data_content_create(u_char **blob, int *len, FILE_FORMAT SpcIndirectDataContent_free(idc); return 0; /* FAILED */ } - idc->data->value->value.sequence->data = p; - idc->data->value->value.sequence->length = l; + if (!ASN1_STRING_set(idc->data->value->value.sequence, p, l)) { + OPENSSL_free(p); + SpcIndirectDataContent_free(idc); + return 0; /* FAILED */ + } + OPENSSL_free(p); idc->messageDigest->digestAlgorithm->algorithm = OBJ_nid2obj(mdtype); idc->messageDigest->digestAlgorithm->parameters = ASN1_TYPE_new(); idc->messageDigest->digestAlgorithm->parameters->type = V_ASN1_NULL; diff --git a/helpers.h b/helpers.h index adaf416..ece3e5e 100644 --- a/helpers.h +++ b/helpers.h @@ -15,6 +15,9 @@ PKCS7 *pkcs7_create(FILE_FORMAT_CTX *ctx); int add_indirect_data_object(PKCS7 *p7); int sign_spc_indirect_data_content(PKCS7 *p7, ASN1_OCTET_STRING *content); PKCS7 *pkcs7_set_content(ASN1_OCTET_STRING *content); +int pkcs7_get_content_digest(PKCS7 *p7, u_char *mdbuf, int *mdtype); +SpcIndirectDataContent *pkcs7_get_indirect_data_content(PKCS7 *p7); +SpcIndirectDataContent *asn1_type_get_indirect_data_content(ASN1_TYPE *content); ASN1_OCTET_STRING *spc_indirect_data_content_get(BIO *hash, FILE_FORMAT_CTX *ctx); int pkcs7_sign_content(PKCS7 *p7, const u_char *data, int len); int asn1_simple_hdr_len(const u_char *p, int len); diff --git a/msi.c b/msi.c index affa65c..fb16841 100644 --- a/msi.c +++ b/msi.c @@ -414,20 +414,7 @@ static int msi_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7) const EVP_MD *md; BIO *hash; - if (is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) { - ASN1_STRING *content_val = p7->d.sign->contents->d.other->value.sequence; - const u_char *p = content_val->data; - SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length); - if (idc) { - if (spc_indirect_data_content_get_digest(idc, mdbuf, &mdtype) < 0) { - fprintf(stderr, "Failed to extract message digest from signature\n\n"); - SpcIndirectDataContent_free(idc); - return 0; /* FAILED */ - } - SpcIndirectDataContent_free(idc); - } - } - if (mdtype == -1) { + if (!pkcs7_get_content_digest(p7, mdbuf, &mdtype)) { fprintf(stderr, "Failed to extract current message digest\n\n"); return 0; /* FAILED */ } diff --git a/osslsigncode.c b/osslsigncode.c index 328fd21..40c4860 100644 --- a/osslsigncode.c +++ b/osslsigncode.c @@ -232,7 +232,7 @@ static ASN1_INTEGER *create_nonce(int bits); static char *clrdp_url_get_x509(X509 *cert); static time_t time_t_get_asn1_time(const ASN1_TIME *s); static time_t time_t_get_si_time(PKCS7_SIGNER_INFO *si); -static ASN1_UTCTIME *asn1_time_get_si_time(PKCS7_SIGNER_INFO *si); +static const ASN1_UTCTIME *asn1_time_get_si_time(PKCS7_SIGNER_INFO *si); static time_t time_t_get_cms_time(CMS_ContentInfo *cms); static CMS_ContentInfo *cms_get_timestamp(PKCS7_SIGNED *p7_signed, PKCS7_SIGNER_INFO *countersignature); @@ -296,7 +296,8 @@ static BIO *bio_encode_rfc3161_request(PKCS7 *p7, const EVP_MD *md) TS_REQ *req = NULL; BIO *bout = NULL, *bhash = NULL; u_char *p; - int len; + const u_char *digest; + int digest_len, len; signer_info = PKCS7_get_signer_info(p7); if (!signer_info) @@ -306,6 +307,9 @@ static BIO *bio_encode_rfc3161_request(PKCS7 *p7, const EVP_MD *md) if (!si) goto out; + digest = ASN1_STRING_get0_data(si->enc_digest); + digest_len = ASN1_STRING_length(si->enc_digest); + bhash = BIO_new(BIO_f_md()); #if defined(__GNUC__) #pragma GCC diagnostic push @@ -319,7 +323,7 @@ static BIO *bio_encode_rfc3161_request(PKCS7 *p7, const EVP_MD *md) #pragma GCC diagnostic pop #endif BIO_push(bhash, BIO_new(BIO_s_null())); - BIO_write(bhash, si->enc_digest->data, si->enc_digest->length); + BIO_write(bhash, digest, digest_len); BIO_gets(bhash, (char*)mdbuf, EVP_MD_size(md)); req = TS_REQ_new(); @@ -377,6 +381,7 @@ static ASN1_INTEGER *create_nonce(int bits) { unsigned char buf[20]; ASN1_INTEGER *nonce = NULL; + BIGNUM *bn = NULL; int len = (bits - 1) / 8 + 1; int i; @@ -391,15 +396,21 @@ static ASN1_INTEGER *create_nonce(int bits) /* Find the first non-zero byte and creating ASN1_INTEGER object. */ for (i = 0; i < len && !buf[i]; ++i) { } - nonce = ASN1_INTEGER_new(); + + bn = BN_bin2bn(buf + i, len - i, NULL); + if (!bn) { + fprintf(stderr, "Could not create nonce BIGNUM\n"); + return NULL; + } + + nonce = BN_to_ASN1_INTEGER(bn, NULL); + BN_free(bn); + if (!nonce) { fprintf(stderr, "Could not create nonce\n"); return NULL; } - OPENSSL_free(nonce->data); - nonce->length = len - i; - nonce->data = OPENSSL_malloc((size_t)nonce->length + 1); - memcpy(nonce->data, buf + i, (size_t)nonce->length); + return nonce; } @@ -1624,7 +1635,7 @@ static int X509_attribute_chain_append_object(STACK_OF(X509_ATTRIBUTE) **unauth_ u_char *p, int len, const char *oid) { X509_ATTRIBUTE *attr = NULL; - ASN1_OBJECT *object; + const ASN1_OBJECT *object; char object_txt[128]; if (*unauth_attr == NULL) { @@ -1635,7 +1646,7 @@ static int X509_attribute_chain_append_object(STACK_OF(X509_ATTRIBUTE) **unauth_ int i; for (i = 0; i < X509at_get_attr_count(*unauth_attr); i++) { attr = X509at_get_attr(*unauth_attr, i); - object = X509_ATTRIBUTE_get0_object(attr); + object = (const ASN1_OBJECT *)X509_ATTRIBUTE_get0_object(attr); if (object == NULL) continue; object_txt[0] = 0x00; @@ -2199,18 +2210,22 @@ static int verify_timestamp_token(PKCS7 *p7, CMS_ContentInfo *timestamp) /* get the embedded content */ pos = CMS_get0_content(timestamp); if (pos != NULL && *pos != NULL) { - const u_char *p = (*pos)->data; - TS_TST_INFO *token = d2i_TS_TST_INFO(NULL, &p, (*pos)->length); + const u_char *p = ASN1_STRING_get0_data(*pos); + int len = ASN1_STRING_length(*pos); + TS_TST_INFO *token = d2i_TS_TST_INFO(NULL, &p, len); if (token) { BIO *bhash; u_char mdbuf[EVP_MAX_MD_SIZE]; ASN1_OCTET_STRING *hash; const ASN1_OBJECT *aoid; - int md_nid; + const u_char *hash_data; + int hash_len, md_nid; const EVP_MD *md; TS_MSG_IMPRINT *msg_imprint = TS_TST_INFO_get_msg_imprint(token); const X509_ALGOR *alg = TS_MSG_IMPRINT_get_algo(msg_imprint); + const u_char *digest = ASN1_STRING_get0_data(si->enc_digest); + int digest_len = ASN1_STRING_length(si->enc_digest); X509_ALGOR_get0(&aoid, NULL, NULL, alg); md_nid = OBJ_obj2nid(aoid); @@ -2232,17 +2247,19 @@ static int verify_timestamp_token(PKCS7 *p7, CMS_ContentInfo *timestamp) #pragma GCC diagnostic pop #endif BIO_push(bhash, BIO_new(BIO_s_null())); - BIO_write(bhash, si->enc_digest->data, si->enc_digest->length); + BIO_write(bhash, digest, digest_len); BIO_gets(bhash, (char*)mdbuf, EVP_MD_size(md)); BIO_free_all(bhash); /* compare the provided hash against the computed hash */ hash =TS_MSG_IMPRINT_get_msg(msg_imprint); - if (memcmp(mdbuf, hash->data, (size_t)hash->length)) { + hash_data = ASN1_STRING_get0_data(hash); + hash_len = ASN1_STRING_length(hash); + if (memcmp(mdbuf, hash_data, (size_t)hash_len)) { printf("Hash value mismatch:\n\tMessage digest algorithm: %s\n", (md_nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(md_nid)); print_hash("\tComputed message digest", "", mdbuf, EVP_MD_size(md)); - print_hash("\tReceived message digest", "", hash->data, hash->length); + print_hash("\tReceived message digest", "", hash_data, hash_len); printf("\nFile's message digest verification: failed\n"); TS_TST_INFO_free(token); return 0; /* FAILED */ @@ -2416,7 +2433,7 @@ static int verify_pkcs7_data(PKCS7 *p7, X509_STORE *store) && (contents->d.other->value.sequence->length > 0)) { if (contents->d.other->type == V_ASN1_SEQUENCE) { /* only verify the content of the sequence */ - const unsigned char *data = contents->d.other->value.sequence->data; + const u_char *data = contents->d.other->value.sequence->data; long len; int inf, tag, class; @@ -2688,11 +2705,12 @@ static time_t time_t_timestamp_get_attributes(CMS_ContentInfo **timestamp, PKCS7 { STACK_OF(PKCS7_SIGNER_INFO) *signer_info; PKCS7_SIGNER_INFO *si; - int md_nid, i; + int md_nid, i, len; STACK_OF(X509_ATTRIBUTE) *auth_attr, *unauth_attr; X509_ATTRIBUTE *attr; - ASN1_OBJECT *object; - ASN1_STRING *value; + const ASN1_OBJECT *object; + const ASN1_STRING *value; + const u_char *data; char object_txt[128]; time_t time = INVALID_TIME; @@ -2711,24 +2729,24 @@ static time_t time_t_timestamp_get_attributes(CMS_ContentInfo **timestamp, PKCS7 printf("\nAuthenticated attributes:\n"); for (i=0; imoreInfo && opus->moreInfo->type == 0) { - char *url = OPENSSL_strdup((char *)opus->moreInfo->value.url->data); - printf("\tURL description: %s\n", url); - OPENSSL_free(url); + ASN1_IA5STRING *url_asn1 = opus->moreInfo->value.url; + const u_char *url_data = ASN1_STRING_get0_data((ASN1_STRING *)url_asn1); + int url_length = ASN1_STRING_length((ASN1_STRING *)url_asn1); + + printf("\tURL description: %.*s\n", url_length, url_data); } if (opus->programName) { char *desc = NULL; + if (opus->programName->type == 0) { - u_char *opusdata; - int len = ASN1_STRING_to_UTF8(&opusdata, opus->programName->value.unicode); - if (len >= 0) { - desc = OPENSSL_strndup((char *)opusdata, (size_t)len); - OPENSSL_free(opusdata); + u_char *opus_data; + int opus_len = ASN1_STRING_to_UTF8(&opus_data, opus->programName->value.unicode); + + if (opus_len >= 0) { + desc = OPENSSL_strndup((char *)opus_data, (size_t)opus_len); + OPENSSL_free(opus_data); } } else { - desc = OPENSSL_strdup((char *)opus->programName->value.ascii->data); + ASN1_IA5STRING *desc_asn1 = opus->programName->value.ascii; + const u_char *desc_data = ASN1_STRING_get0_data((ASN1_STRING *)desc_asn1); + int desc_len = ASN1_STRING_length((ASN1_STRING *)desc_asn1); + + desc = OPENSSL_strndup((const char *)desc_data, (size_t)desc_len); } if (desc) { printf("\tText description: %s\n", desc); @@ -2769,31 +2796,31 @@ static time_t time_t_timestamp_get_attributes(CMS_ContentInfo **timestamp, PKCS7 SpcSpOpusInfo_free(opus); } else if (!strcmp(object_txt, SPC_STATEMENT_TYPE_OBJID)) { /* Microsoft OID: 1.3.6.1.4.1.311.2.1.11 */ - const u_char *purpose; - value = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL); + value = (const ASN1_STRING *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL); if (value == NULL) continue; - purpose = ASN1_STRING_get0_data(value); - if (!memcmp(purpose, purpose_comm, sizeof purpose_comm)) + data = ASN1_STRING_get0_data(value); + if (!memcmp(data, purpose_comm, sizeof purpose_comm)) printf("\tMicrosoft Commercial Code Signing purpose\n"); - else if (!memcmp(purpose, purpose_ind, sizeof purpose_ind)) + else if (!memcmp(data, purpose_ind, sizeof purpose_ind)) printf("\tMicrosoft Individual Code Signing purpose\n"); else printf("\tUnrecognized Code Signing purpose\n"); } else if (!strcmp(object_txt, MS_JAVA_SOMETHING)) { /* Microsoft OID: 1.3.6.1.4.1.311.15.1 */ - const u_char *level; - value = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL); + value = (const ASN1_STRING *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL); if (value == NULL) continue; - level = ASN1_STRING_get0_data(value); - if (!memcmp(level, java_attrs_low, sizeof java_attrs_low)) + data = ASN1_STRING_get0_data(value); + if (!memcmp(data, java_attrs_low, sizeof java_attrs_low)) printf("\tLow level of permissions in Microsoft Internet Explorer 4.x for CAB files\n"); else printf("\tUnrecognized level of permissions in Microsoft Internet Explorer 4.x for CAB files\n"); } else if (!strcmp(object_txt, PKCS9_SEQUENCE_NUMBER)) { /* PKCS#9 sequence number - Policy OID: 1.2.840.113549.1.9.25.4 */ - ASN1_INTEGER *number = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_INTEGER, NULL); + const ASN1_INTEGER *number; + + number = (const ASN1_INTEGER *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_INTEGER, NULL); if (number == NULL) continue; printf("\tSequence number: %ld\n", ASN1_INTEGER_get(number)); @@ -2804,17 +2831,17 @@ static time_t time_t_timestamp_get_attributes(CMS_ContentInfo **timestamp, PKCS7 unauth_attr = PKCS7_get_attributes(si); /* cont[1] */ for (i=0; idata, blob->length); + char *data_blob; + + data = ASN1_STRING_get0_data(value); + data_blob = OPENSSL_buf2hexstr(data, len); + printf("\nUnauthenticated Data Blob:\n%s\n", data_blob); OPENSSL_free(data_blob); } else { - printf("\nUnauthenticated Data Blob length: %d bytes\n", blob->length); + printf("\nUnauthenticated Data Blob length: %d bytes\n", len); } } } /* Signature */ if (verbose) { + data = ASN1_STRING_get0_data(si->enc_digest); + len = ASN1_STRING_length(si->enc_digest); + md_nid = OBJ_obj2nid(si->digest_enc_alg->algorithm); printf("\nDigest encryption algorithm: %s\n", (md_nid == NID_undef) ? "UNKNOWN" : OBJ_nid2sn(md_nid)); - print_hash("Signature", "", ASN1_STRING_get0_data(si->enc_digest), ASN1_STRING_length(si->enc_digest)); + print_hash("Signature", "", data, len); } return time; @@ -2928,7 +2963,7 @@ static time_t time_t_get_asn1_time(const ASN1_TIME *s) */ static time_t time_t_get_si_time(PKCS7_SIGNER_INFO *si) { - ASN1_UTCTIME *time = asn1_time_get_si_time(si); + const ASN1_UTCTIME *time = asn1_time_get_si_time(si); if (time == NULL) return INVALID_TIME; /* FAILED */ @@ -2940,7 +2975,7 @@ static time_t time_t_get_si_time(PKCS7_SIGNER_INFO *si) * [in] si: PKCS7_SIGNER_INFO structure * [returns] NULL on error or ASN1_UTCTIME on success */ -static ASN1_UTCTIME *asn1_time_get_si_time(PKCS7_SIGNER_INFO *si) +static const ASN1_UTCTIME *asn1_time_get_si_time(PKCS7_SIGNER_INFO *si) { STACK_OF(X509_ATTRIBUTE) *auth_attr = PKCS7_get_signed_attributes(si); if (auth_attr) { @@ -2950,7 +2985,7 @@ static ASN1_UTCTIME *asn1_time_get_si_time(PKCS7_SIGNER_INFO *si) X509_ATTRIBUTE *attr = X509at_get_attr(auth_attr, i); if (OBJ_obj2nid(X509_ATTRIBUTE_get0_object(attr)) == nid) { /* PKCS#9 signing time - Policy OID: 1.2.840.113549.1.9.5 */ - return X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_UTCTIME, NULL); + return (const ASN1_UTCTIME *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_UTCTIME, NULL); } } } @@ -2990,10 +3025,13 @@ static time_t time_t_get_cms_time(CMS_ContentInfo *cms) ASN1_OCTET_STRING **pos = CMS_get0_content(cms); if (pos != NULL && *pos != NULL) { - const u_char *p = (*pos)->data; - TS_TST_INFO *token = d2i_TS_TST_INFO(NULL, &p, (*pos)->length); + const u_char *p = ASN1_STRING_get0_data(*pos); + int len = ASN1_STRING_length(*pos); + TS_TST_INFO *token = d2i_TS_TST_INFO(NULL, &p, len); + if (token) { const ASN1_GENERALIZEDTIME *asn1_time = TS_TST_INFO_get_time(token); + posix_time = time_t_get_asn1_time(asn1_time); TS_TST_INFO_free(token); } @@ -3342,11 +3380,11 @@ static STACK_OF(PKCS7) *signature_list_create(PKCS7 *p7) int j; for (j=0; j + +# AUTHORS + +Originally written by Per Allansson. + +Maintained and extended by Michał Trojnara. + +Major contributions by Małgorzata Olszówka. + +Additional contributions by other project contributors. + +# SEE ALSO + +**OpenSSL** Library + + + diff --git a/pe.c b/pe.c index 0422bd4..e3ff02e 100644 --- a/pe.c +++ b/pe.c @@ -87,6 +87,7 @@ static uint32_t pe_calc_checksum(BIO *bio, uint32_t header_size); static uint32_t pe_calc_realchecksum(FILE_FORMAT_CTX *ctx); static int pe_modify_header(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata); static BIO *pe_digest_calc_bio(FILE_FORMAT_CTX *ctx, const EVP_MD *md); +static int pkcs7_get_page_hash(PKCS7 *p7, u_char **ph, int *phlen, int *phtype); static int pe_page_hash_get(u_char **ph, int *phlen, int *phtype, SpcAttributeTypeAndOptionalValue *obj); static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype); static int pe_verify_page_hash(FILE_FORMAT_CTX *ctx, u_char *ph, int phlen, int phtype); @@ -247,51 +248,34 @@ static int pe_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7) u_char *cmdbuf = NULL; u_char *ph = NULL; - if (is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) { - ASN1_STRING *content_val = p7->d.sign->contents->d.other->value.sequence; - const u_char *p = content_val->data; - SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length); - if (idc) { - if (!pe_page_hash_get(&ph, &phlen, &phtype, idc->data)) { - fprintf(stderr, "Failed to extract a page hash\n\n"); - SpcIndirectDataContent_free(idc); - return 0; /* FAILED */ - } - if (spc_indirect_data_content_get_digest(idc, mdbuf, &mdtype) < 0) { - fprintf(stderr, "Failed to extract message digest from signature\n\n"); - OPENSSL_free(ph); - SpcIndirectDataContent_free(idc); - return 0; /* FAILED */ - } - SpcIndirectDataContent_free(idc); - } - } - if (mdtype == -1) { + if (!pkcs7_get_content_digest(p7, mdbuf, &mdtype)) { fprintf(stderr, "Failed to extract current message digest\n\n"); - OPENSSL_free(ph); return 0; /* FAILED */ } md = EVP_get_digestbynid(mdtype); cmdbuf = pe_digest_calc(ctx, md); if (!cmdbuf) { fprintf(stderr, "Failed to calculate message digest\n\n"); - OPENSSL_free(ph); return 0; /* FAILED */ } if (!compare_digests(mdbuf, cmdbuf, mdtype)) { fprintf(stderr, "Signature verification: failed\n\n"); - OPENSSL_free(ph); OPENSSL_free(cmdbuf); return 0; /* FAILED */ } + OPENSSL_free(cmdbuf); + + if (!pkcs7_get_page_hash(p7, &ph, &phlen, &phtype)) { + fprintf(stderr, "Failed to extract page hash\n\n"); + return 0; /* FAILED */ + } if (!pe_verify_page_hash(ctx, ph, phlen, phtype)) { fprintf(stderr, "Signature verification: failed\n\n"); OPENSSL_free(ph); - OPENSSL_free(cmdbuf); return 0; /* FAILED */ } OPENSSL_free(ph); - OPENSSL_free(cmdbuf); + return 1; /* OK */ } @@ -839,6 +823,36 @@ static BIO *pe_digest_calc_bio(FILE_FORMAT_CTX *ctx, const EVP_MD *md) * Page hash support */ +/* + * Retrieve a page hash from PKCS7 SPC_INDIRECT_DATA structure. + * [in] p7: PKCS7 signature + * [out] ph: page hash + * [out] phlen: page hash length + * [out] phtype: NID_sha1 or NID_sha256 + * [returns] 0 on error or 1 on success + */ +static int pkcs7_get_page_hash(PKCS7 *p7, u_char **ph, int *phlen, int *phtype) +{ + SpcIndirectDataContent *idc = pkcs7_get_indirect_data_content(p7); + + if (!idc) { + fprintf(stderr, "Failed to decode SpcIndirectDataContent\n\n"); + return 0; /* FAILED */ + } + if (!idc->data) { + fprintf(stderr, "Missing SpcIndirectDataContent data\n\n"); + SpcIndirectDataContent_free(idc); + return 0; /* FAILED */ + } + if (!pe_page_hash_get(ph, phlen, phtype, idc->data)) { + fprintf(stderr, "Failed to extract a page hash\n\n"); + SpcIndirectDataContent_free(idc); + return 0; /* FAILED */ + } + SpcIndirectDataContent_free(idc); + return 1; /* OK */ +} + /* * Retrieve a page hash from SPC_INDIRECT_DATA structure. * [out] ph: page hash @@ -847,43 +861,71 @@ static BIO *pe_digest_calc_bio(FILE_FORMAT_CTX *ctx, const EVP_MD *md) * [in] obj: SPC_INDIRECT_DATA OID: 1.3.6.1.4.1.311.2.1.4 containing page hash * [returns] 0 on error or 1 on success */ -static int pe_page_hash_get(u_char **ph, int *phlen, int *phtype, SpcAttributeTypeAndOptionalValue *obj) +static int pe_page_hash_get(u_char **ph, int *phlen, int *phtype, + SpcAttributeTypeAndOptionalValue *obj) { - const u_char *blob; + const unsigned char *blob; + const unsigned char *sequence_data; + const unsigned char *classid_data; + const unsigned char *serialized_data; SpcPeImageData *id; SpcSerializedObject *so; - int l, l2; + int sequence_len, classid_len, serialized_len, l, l2; char buf[128]; + /* Validate input object */ if (!obj || !obj->value) return 0; /* FAILED */ - blob = obj->value->value.sequence->data; - id = d2i_SpcPeImageData(NULL, &blob, obj->value->value.sequence->length); - if (!id) { + + /* Decode SpcPeImageData from ASN.1 sequence */ + sequence_data = ASN1_STRING_get0_data(obj->value->value.sequence); + sequence_len = ASN1_STRING_length(obj->value->value.sequence); + + /* d2i_* modifies the input pointer, so use a temporary variable */ + blob = sequence_data; + id = d2i_SpcPeImageData(NULL, &blob, sequence_len); + if (!id) return 0; /* FAILED */ - } + + /* Validate SpcPeImageData contents */ if (!id->file) { SpcPeImageData_free(id); return 0; /* FAILED */ } + + /* Type 1 means SpcSerializedObject */ if (id->file->type != 1) { SpcPeImageData_free(id); - return 1; /* OK - This is not SpcSerializedObject structure that contains page hashes */ + return 1; /* OK - no page hashes present */ } + so = id->file->value.moniker; - if (so->classId->length != sizeof classid_page_hash || - memcmp(so->classId->data, classid_page_hash, sizeof classid_page_hash)) { + + /* Validate serialized object class ID */ + classid_data = ASN1_STRING_get0_data((ASN1_STRING *)so->classId); + classid_len = ASN1_STRING_length((ASN1_STRING *)so->classId); + + if (classid_len != sizeof classid_page_hash || + memcmp(classid_data, classid_page_hash, sizeof classid_page_hash)) { SpcPeImageData_free(id); return 0; /* FAILED */ } - /* skip ASN.1 SET hdr */ - l = asn1_simple_hdr_len(so->serializedData->data, so->serializedData->length); - blob = so->serializedData->data + l; - obj = d2i_SpcAttributeTypeAndOptionalValue(NULL, &blob, so->serializedData->length - l); + + /*Get serialized ASN.1 blob */ + serialized_data = ASN1_STRING_get0_data((ASN1_STRING *)so->serializedData); + serialized_len = ASN1_STRING_length((ASN1_STRING *)so->serializedData); + + /* Skip ASN.1 SET header */ + l = asn1_simple_hdr_len(serialized_data, serialized_len); + blob = serialized_data + l; + + /* Decode nested SpcAttributeTypeAndOptionalValue */ + obj = d2i_SpcAttributeTypeAndOptionalValue(NULL, &blob, serialized_len - l); SpcPeImageData_free(id); if (!obj) return 0; /* FAILED */ + /* Determine page hash algorithm */ *phtype = 0; buf[0] = 0x00; OBJ_obj2txt(buf, sizeof buf, obj->type, 1); @@ -895,15 +937,30 @@ static int pe_page_hash_get(u_char **ph, int *phlen, int *phtype, SpcAttributeTy SpcAttributeTypeAndOptionalValue_free(obj); return 0; /* FAILED */ } - /* Skip ASN.1 SET hdr */ - l2 = asn1_simple_hdr_len(obj->value->value.sequence->data, obj->value->value.sequence->length); - /* Skip ASN.1 OCTET STRING hdr */ - l = asn1_simple_hdr_len(obj->value->value.sequence->data + l2, obj->value->value.sequence->length - l2); + + /* IMPORTANT: + * obj now points to the newly decoded structure, + * so refresh sequence_data/sequence_len */ + sequence_data = ASN1_STRING_get0_data(obj->value->value.sequence); + sequence_len = ASN1_STRING_length(obj->value->value.sequence); + + /* Skip ASN.1 SET header */ + l2 = asn1_simple_hdr_len(sequence_data, sequence_len); + + /* Skip ASN.1 OCTET STRING header */ + l = asn1_simple_hdr_len(sequence_data + l2, sequence_len - l2); l += l2; - *phlen = obj->value->value.sequence->length - l; + + /* Extract raw page hash blob */ + *phlen = sequence_len - l; *ph = OPENSSL_malloc((size_t)*phlen); - memcpy(*ph, obj->value->value.sequence->data + l, (size_t)*phlen); + if (!*ph) { + SpcAttributeTypeAndOptionalValue_free(obj); + return 0; /* FAILED */ + } + memcpy(*ph, sequence_data + l, (size_t)*phlen); SpcAttributeTypeAndOptionalValue_free(obj); + return 1; /* OK */ } diff --git a/script.c b/script.c index 457791b..6148800 100644 --- a/script.c +++ b/script.c @@ -288,21 +288,7 @@ static int script_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7) const EVP_MD *md; BIO *bhash; - /* FIXME: this shared code most likely belongs in osslsigncode.c */ - if (is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) { - ASN1_STRING *content_val = p7->d.sign->contents->d.other->value.sequence; - const u_char *p = content_val->data; - SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length); - if (idc) { - if (spc_indirect_data_content_get_digest(idc, mdbuf, &mdtype) < 0) { - fprintf(stderr, "Failed to extract message digest from signature\n\n"); - SpcIndirectDataContent_free(idc); - return 0; /* FAILED */ - } - SpcIndirectDataContent_free(idc); - } - } - if (mdtype == -1) { + if (!pkcs7_get_content_digest(p7, mdbuf, &mdtype)) { fprintf(stderr, "Failed to extract current message digest\n\n"); return 0; /* FAILED */ }