diff --git a/osslsigncode.c b/osslsigncode.c index 2bee061c..e4657ecc 100644 --- a/osslsigncode.c +++ b/osslsigncode.c @@ -1173,14 +1173,14 @@ static void print_cert(X509 *cert, int i) issuer = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0); serialbn = ASN1_INTEGER_to_BN(X509_get_serialNumber(cert), NULL); serial = BN_bn2hex(serialbn); - if (i > 0) - printf("\t------------------\n"); + printf("\t------------------\n"); printf("\tSigner #%d:\n\t\tSubject: %s\n\t\tIssuer : %s\n\t\tSerial : %s\n\t\tCertificate expiration date:\n", i, subject, issuer, serial); printf("\t\t\tnotBefore : "); print_asn1_time(X509_get0_notBefore(cert)); printf("\t\t\tnotAfter : "); print_asn1_time(X509_get0_notAfter(cert)); + printf("\n"); OPENSSL_free(subject); OPENSSL_free(issuer); @@ -1189,24 +1189,106 @@ static void print_cert(X509 *cert, int i) } /* - * Print X509 certificate list from p7->d.sign->cert - * [in] p7: PKCS#7 signature - * [returns] 1 on success + * [in] txt, list + * [returns] 0 on error or 1 on success */ -static int print_certs(PKCS7 *p7) +static int on_list(const char *txt, const char *list[]) { - X509 *cert; - int i, count; - - count = sk_X509_num(p7->d.sign->cert); - printf("\nNumber of certificates: %d\n", count); - for (i=0; id.sign->cert, i); - if (!cert) - return 0; /* FAILED */ - print_cert(cert, i); + while (*list) + if (!strcmp(txt, *list++)) + return 1; /* OK */ + return 0; /* FAILED */ +} + +/* + * Check Windows certificate whitelist: + * https://learn.microsoft.com/en-us/troubleshoot/windows-server/identity/trusted-root-certificates-are-required + * For Microsoft Root Authority, serial number: 00C1008B3C3C8811D13EF663ECDF40, + * fingerprint: "F3:84:06:E5:40:D7:A9:D9:0C:B4:A9:47:92:99:64:0F:FB:6D:F9:E2:24:EC:C7:A0:1C:0D:95:58:D8:DA:D7:7D" + * expiration date: 12/31/2020, intended purposes: All, + * ignore X509_V_ERR_INVALID_CA and X509_V_ERR_CERT_HAS_EXPIRED + * [in] cert: X509 certificate + * [in] error: error code + * [returns] 0 on error or 1 on success + */ +static int trusted_cert(X509 *cert, int error) { + const char *fingerprints[] = { + "F3:84:06:E5:40:D7:A9:D9:0C:B4:A9:47:92:99:64:0F:FB:6D:F9:E2:24:EC:C7:A0:1C:0D:95:58:D8:DA:D7:7D", + NULL + }; + u_char mdbuf[EVP_MAX_MD_SIZE], *p; + char *hex = NULL; + int len; + const EVP_MD *md = EVP_get_digestbynid(NID_sha256); + BIO *bhash = BIO_new(BIO_f_md()); + + if (!BIO_set_md(bhash, md)) { + BIO_free_all(bhash); + return 0; /* FAILED */ } - return 1; /* OK */ + BIO_push(bhash, BIO_new(BIO_s_null())); + len = i2d_X509(cert, NULL); + p = OPENSSL_malloc((size_t)len); + i2d_X509(cert, &p); + p -= len; + BIO_write(bhash, p, len); + OPENSSL_free(p); + BIO_gets(bhash, (char *)mdbuf, EVP_MD_size(md)); + BIO_free_all(bhash); + + hex = OPENSSL_buf2hexstr(mdbuf, (size_t)EVP_MD_size(md)); + if (!hex) { + return 0; /* FAILED */ + } + if (on_list(hex, fingerprints)) { + printf("\tWarning: Ignoring %s error for Windows certificate whitelist\n", + X509_verify_cert_error_string(error)); + OPENSSL_free(hex); + return 1; /* trusted */ + } + OPENSSL_free(hex); + return 0; /* untrusted */ +} + +/* + * X509_STORE_CTX_verify_cb + */ +static int verify_ca_callback(int ok, X509_STORE_CTX *ctx) +{ + int error = X509_STORE_CTX_get_error(ctx); + int depth = X509_STORE_CTX_get_error_depth(ctx); + + X509 *current_cert = X509_STORE_CTX_get_current_cert(ctx); + print_cert(current_cert, depth); + if (!ok) { + if (trusted_cert(current_cert, error)) { + return 1; + } else { + printf("\tError: %s\n", X509_verify_cert_error_string(error)); + } + } + return ok; +} + +static int verify_crl_callback(int ok, X509_STORE_CTX *ctx) +{ + int error = X509_STORE_CTX_get_error(ctx); + int depth = X509_STORE_CTX_get_error_depth(ctx); + + X509 *current_cert = X509_STORE_CTX_get_current_cert(ctx); + print_cert(current_cert, depth); + if (!ok) { + if (trusted_cert(current_cert, error)) { + return 1; + } else if (error == X509_V_ERR_CERT_HAS_EXPIRED) { + printf("\tWarning: Ignoring %s error for CRL validation\n", + X509_verify_cert_error_string(error)); + return 1; + } else { + printf("\tError: %s\n", X509_verify_cert_error_string(error)); + } + } + return ok; } /* @@ -1233,30 +1315,11 @@ static int x509_store_load_file(X509_STORE *store, char *cafile) return 0; /* FAILED */ if (!X509_STORE_set1_param(store, param)) return 0; /* FAILED */ + X509_STORE_set_verify_cb(store, verify_ca_callback); return 1; /* OK */ } -/* X509_STORE_CTX_verify_cb */ -static int verify_callback(int ok, X509_STORE_CTX *ctx) -{ - int error = X509_STORE_CTX_get_error(ctx); - int depth = X509_STORE_CTX_get_error_depth(ctx); - - if (!ok && error == X509_V_ERR_CERT_HAS_EXPIRED) { - if (depth == 0) { - printf("\nWarning: Ignoring expired signer certificate for CRL validation\n"); - return 1; - } else { - X509 *current_cert = X509_STORE_CTX_get_current_cert(ctx); - printf("\nError: Expired CA certificate:\n"); - print_cert(current_cert, 0); - printf("\n"); - } - } - return ok; -} - /* * [in, out] store: structure for holding information about X.509 certificates and CRLs * [in] cafile: file contains concatenated CA certificates in PEM format @@ -1287,7 +1350,7 @@ static int x509_store_load_crlfile(X509_STORE *store, char *cafile, char *crlfil return 0; /* FAILED */ if (!X509_STORE_set1_param(store, param)) return 0; /* FAILED */ - X509_STORE_set_verify_cb(store, verify_callback); + X509_STORE_set_verify_cb(store, verify_crl_callback); return 1; /* OK */ } @@ -1326,6 +1389,7 @@ static int verify_crl(char *cafile, char *crlfile, STACK_OF(X509_CRL) *crls, if (crls) X509_STORE_CTX_set0_crls(ctx, crls); + printf("\nCertificate Revocation List verified by:\n"); if (X509_verify_cert(ctx) <= 0) { int error = X509_STORE_CTX_get_error(ctx); printf("\nX509_verify_cert: certificate verify error: %s\n", @@ -1567,6 +1631,7 @@ static int verify_timestamp(FILE_FORMAT_CTX *ctx, PKCS7 *p7, CMS_ContentInfo *ti } /* verify a CMS SignedData structure */ + printf("\nTimestamp verified by:\n"); if (!CMS_verify(timestamp, NULL, store, 0, NULL, 0)) { printf("\nCMS_verify error\n"); X509_STORE_free(store); @@ -1680,6 +1745,7 @@ static int verify_authenticode(FILE_FORMAT_CTX *ctx, PKCS7 *p7, time_t time, X50 bio = BIO_new_mem_buf(p7->d.sign->contents->d.other->value.sequence->data, p7->d.sign->contents->d.other->value.sequence->length); } + printf("\nSigning Certificate Chain:\n"); if (!PKCS7_verify(p7, NULL, store, bio, NULL, 0)) { printf("\nPKCS7_verify error\n"); X509_STORE_free(store); @@ -1812,6 +1878,7 @@ static int print_cms_timestamp(CMS_ContentInfo *timestamp, time_t time) { STACK_OF(CMS_SignerInfo) *sinfos; CMS_SignerInfo *si; + X509_ATTRIBUTE *attr; int md_nid; ASN1_INTEGER *serialno; char *issuer_name, *serial; @@ -1825,19 +1892,28 @@ static int print_cms_timestamp(CMS_ContentInfo *timestamp, time_t time) si = sk_CMS_SignerInfo_value(sinfos, 0); if (si == NULL) return 0; /* FAILED */ - printf("\nThe signature is timestamped: "); + printf("\nCountersignatures:\n\tTimestamp time: "); print_time_t(time); + + /* PKCS#9 signing time - Policy OID: 1.2.840.113549.1.9.5 */ + attr = CMS_signed_get_attr(si, CMS_signed_get_attr_by_NID(si, NID_pkcs9_signingTime, -1)); + if (attr == NULL) + return 0; /* FAILED */ + printf("\tSigning time: "); + print_time_t(time_t_get_asn1_time(X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_UTCTIME, NULL))); + CMS_SignerInfo_get0_algs(si, NULL, NULL, &pdig, NULL); if (pdig == NULL || pdig->algorithm == NULL) return 0; /* FAILED */ md_nid = OBJ_obj2nid(pdig->algorithm); - printf("Hash Algorithm: %s\n", (md_nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(md_nid)); + printf("\tHash Algorithm: %s\n", (md_nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(md_nid)); + if (!CMS_SignerInfo_get0_signer_id(si, NULL, &issuer, &serialno) || !issuer) return 0; /* FAILED */ issuer_name = X509_NAME_oneline(issuer, NULL, 0); serialbn = ASN1_INTEGER_to_BN(serialno, NULL); serial = BN_bn2hex(serialbn); - printf("Timestamp Verified by:\n\t\tIssuer : %s\n\t\tSerial : %s\n", issuer_name, serial); + printf("\tIssuer: %s\n\tSerial: %s\n", issuer_name, serial); OPENSSL_free(issuer_name); BN_free(serialbn); OPENSSL_free(serial); @@ -1871,7 +1947,7 @@ static time_t time_t_timestamp_get_attributes(CMS_ContentInfo **timestamp, PKCS7 if (!si) return INVALID_TIME; /* FAILED */ md_nid = OBJ_obj2nid(si->digest_alg->algorithm); - printf("\nMessage digest algorithm: %s\n", + printf("Message digest algorithm: %s\n", (md_nid == NID_undef) ? "UNKNOWN" : OBJ_nid2sn(md_nid)); printf("\nAuthenticated attributes:\n"); auth_attr = PKCS7_get_signed_attributes(si); /* cont[0] */ @@ -2336,8 +2412,6 @@ static int verify_signature(FILE_FORMAT_CTX *ctx, PKCS7 *p7) printf("Signer's certificate:\n"); print_cert(signer, 0); - if (!print_certs(p7)) - printf("Print certs error\n"); time = time_t_timestamp_get_attributes(×tamp, p7, ctx->options->verbose); if (ctx->options->leafhash != NULL) { leafok = verify_leaf_hash(signer, ctx->options->leafhash); @@ -2358,7 +2432,7 @@ static int verify_signature(FILE_FORMAT_CTX *ctx, PKCS7 *p7) printf("TSA's CRL file: %s\n", ctx->options->tsa_crlfile); if (timestamp) { if (ctx->options->ignore_timestamp) { - printf("\nTimestamp Server Signature verification is disabled\n\n"); + printf("\nTimestamp Server Signature verification is disabled\n"); time = INVALID_TIME; } else { int timeok = verify_timestamp(ctx, p7, timestamp, time); @@ -2620,19 +2694,6 @@ static void free_options(GLOBAL_OPTIONS *options) options->crls = NULL; } - -/* - * [in] txt, list - * [returns] 0 on error or 1 on success - */ -static int on_list(const char *txt, const char *list[]) -{ - while (*list) - if (!strcmp(txt, *list++)) - return 1; /* OK */ - return 0; /* FAILED */ -} - /* * [in] argv0, cmd * [returns] none