diff --git a/src/ssl_p7p12.c b/src/ssl_p7p12.c index 51054006f41..617e4b0cfb3 100644 --- a/src/ssl_p7p12.c +++ b/src/ssl_p7p12.c @@ -251,6 +251,37 @@ WOLFSSL_STACK* wolfSSL_PKCS7_to_stack(PKCS7* pkcs7) return NULL; } +/* Return 1 if the DER certificate is equal to on of 'certs' passed in. */ +static int wolfSSL_PKCS7_CertInStack(WOLFSSL_STACK* certs, const byte* der, + word32 derSz) +{ + int i; + int num; + + if (certs == NULL || der == NULL || derSz == 0) { + return 0; + } + + num = wolfSSL_sk_X509_num(certs); + for (i = 0; i < num; i++) { + WOLFSSL_X509* x509; + const byte* certDer = NULL; + int certDerSz = 0; + + x509 = wolfSSL_sk_X509_value(certs, i); + if (x509 != NULL) { + certDer = wolfSSL_X509_get_der(x509, &certDerSz); + } + if (certDer != NULL && certDerSz > 0 && (word32)certDerSz == derSz && + XMEMCMP(certDer, der, derSz) == 0) { + return 1; + } + } + + return 0; +} + + /** * Return stack of signers contained in PKCS7 cert. * Notes: @@ -258,7 +289,8 @@ WOLFSSL_STACK* wolfSSL_PKCS7_to_stack(PKCS7* pkcs7) * - Returned WOLFSSL_STACK must be freed by caller. * * pkcs7 - PKCS7 struct to retrieve signer certs from. - * certs - currently unused + * certs - certificates the signer may be taken from. With PKCS7_NOINTERN the + * signer must be one of these. * flags - flags to control function behavior. * * Return WOLFSSL_STACK of signers on success, NULL on error. @@ -276,8 +308,13 @@ WOLFSSL_STACK* wolfSSL_PKCS7_get0_signers(PKCS7* pkcs7, WOLFSSL_STACK* certs, return NULL; if (flags & PKCS7_NOINTERN) { - WOLFSSL_MSG("PKCS7_NOINTERN flag not supported"); - return NULL; + /* With this flag the signer must be in certs stack. A signer that is + * only embedded in the message is rejected. */ + if (!wolfSSL_PKCS7_CertInStack(certs, p7->pkcs7.verifyCert, + p7->pkcs7.verifyCertSz)) { + WOLFSSL_MSG("PKCS7_NOINTERN: verified signer not in certs"); + return NULL; + } } /* Prefer the certificate that actually verified the signature. Falling @@ -315,8 +352,6 @@ WOLFSSL_STACK* wolfSSL_PKCS7_get0_signers(PKCS7* pkcs7, WOLFSSL_STACK* certs, return NULL; } - (void)certs; - return signers; } @@ -805,13 +840,6 @@ int wolfSSL_PKCS7_verify(PKCS7* pkcs7, WOLFSSL_STACK* certs, p7->pkcs7.contentSz = (word32)memSz; } - /* certs is the list of certificates to find the cert with issuer/serial. */ - (void)certs; - /* store is the certificate store to use to verify signer certificate - * associated with the signers. - */ - (void)store; - ret = wc_PKCS7_VerifySignedData(&p7->pkcs7, p7->data, p7->len); if (ret != 0) return WOLFSSL_FAILURE; @@ -829,6 +857,15 @@ int wolfSSL_PKCS7_verify(PKCS7* pkcs7, WOLFSSL_STACK* certs, return WOLFSSL_FAILURE; } + /* A signer lookup happens regardless of PKCS7_NOVERIFY. With PKCS7_NOINTERN + * the signer must be in the certs stack. */ + if ((flags & PKCS7_NOINTERN) && + !wolfSSL_PKCS7_CertInStack(certs, p7->pkcs7.verifyCert, + p7->pkcs7.verifyCertSz)) { + WOLFSSL_MSG("PKCS7_NOINTERN: verified signer not in certs"); + return WOLFSSL_FAILURE; + } + if ((flags & PKCS7_NOVERIFY) != PKCS7_NOVERIFY) { /* Verify signer certificates */ if (store == NULL || store->cm == NULL) { @@ -851,7 +888,10 @@ int wolfSSL_PKCS7_verify(PKCS7* pkcs7, WOLFSSL_STACK* certs, for (i = 0; i < wolfSSL_sk_X509_num(signers); i++) { signer = wolfSSL_sk_X509_value(signers, i); - if (wolfSSL_X509_STORE_CTX_init(ctx, store, signer, NULL) + + /* Trust anchors come from the store and untrusted intermediates + * from the certs stack. */ + if (wolfSSL_X509_STORE_CTX_init(ctx, store, signer, certs) != WOLFSSL_SUCCESS) { WOLFSSL_MSG("Failed to initialize X509 STORE CTX"); wolfSSL_sk_X509_pop_free(signers, NULL); diff --git a/tests/api/test_ossl_p7p12.c b/tests/api/test_ossl_p7p12.c index 4cc99c3bb31..328904d9940 100644 --- a/tests/api/test_ossl_p7p12.c +++ b/tests/api/test_ossl_p7p12.c @@ -1466,6 +1466,131 @@ int test_wolfSSL_SMIME_write_PKCS7(void) return EXPECT_RESULT(); } +/* PKCS7_NOINTERN means the signer must be found in the certs argument, not + * among the certificates embedded in the message. + */ +int test_wolfSSL_PKCS7_verify_nointern(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_ALL) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \ + !defined(NO_BIO) && !defined(NO_FILESYSTEM) && defined(HAVE_SMIME) + byte data[] = "Test data to encode."; + const char* cert = "./certs/server-cert.pem"; + const char* key = "./certs/server-key.pem"; + const char* ca = "./certs/ca-cert.pem"; + int signFlags = PKCS7_DETACHED | PKCS7_STREAM; + WOLFSSL_BIO* certBio = NULL; + WOLFSSL_BIO* keyBio = NULL; + WOLFSSL_BIO* caBio = NULL; + WOLFSSL_BIO* inBio = NULL; + WOLFSSL_BIO* smimeBio = NULL; + WOLFSSL_BIO* readBio = NULL; + WOLFSSL_BIO* content = NULL; + X509* signCert = NULL; + EVP_PKEY* signKey = NULL; + X509* caCert = NULL; + X509_STORE* store = NULL; + STACK_OF(X509)* signerSk = NULL; + STACK_OF(X509)* caSk = NULL; + STACK_OF(X509)* signers = NULL; + PKCS7* p7 = NULL; + PKCS7* p7Ver = NULL; + const byte* smime = NULL; + int smimeSz = 0; + int i; + struct { + int certsSel; /* 0 = NULL, 1 = { ca-cert }, 2 = { server-cert } */ + int useStore; + int flags; + int expectOk; + } cases[] = { + /* Signer only embedded in the message: rejected. */ + { 1, 0, PKCS7_NOVERIFY | PKCS7_NOINTERN, 0 }, + { 0, 0, PKCS7_NOVERIFY | PKCS7_NOINTERN, 0 }, + { 1, 1, PKCS7_NOINTERN, 0 }, + /* Signer present in certs: accepted. */ + { 2, 0, PKCS7_NOVERIFY | PKCS7_NOINTERN, 1 }, + { 2, 1, PKCS7_NOINTERN, 1 }, + /* Without PKCS7_NOINTERN the embedded signer may be used. */ + { 1, 0, PKCS7_NOVERIFY, 1 }, + }; + + ExpectNotNull(certBio = BIO_new_file(cert, "r")); + ExpectNotNull(keyBio = BIO_new_file(key, "r")); + ExpectNotNull(caBio = BIO_new_file(ca, "r")); + ExpectNotNull(signCert = PEM_read_bio_X509(certBio, NULL, 0, NULL)); + ExpectNotNull(signKey = PEM_read_bio_PrivateKey(keyBio, NULL, 0, NULL)); + ExpectNotNull(caCert = PEM_read_bio_X509(caBio, NULL, 0, NULL)); + ExpectNotNull(store = X509_STORE_new()); + ExpectIntEQ(X509_STORE_add_cert(store, caCert), 1); + + /* Stacks only borrow the certs; freed with sk_X509_free(). */ + ExpectNotNull(signerSk = sk_X509_new_null()); + ExpectIntGT(sk_X509_push(signerSk, signCert), 0); + ExpectNotNull(caSk = sk_X509_new_null()); + ExpectIntGT(sk_X509_push(caSk, caCert), 0); + + /* Detached S/MIME message signed by server-cert, which is embedded. */ + ExpectNotNull(inBio = BIO_new(BIO_s_mem())); + ExpectIntGT(BIO_write(inBio, data, sizeof(data)), 0); + ExpectNotNull(p7 = PKCS7_sign(signCert, signKey, NULL, inBio, signFlags)); + ExpectNotNull(smimeBio = BIO_new(BIO_s_mem())); + ExpectIntEQ(SMIME_write_PKCS7(smimeBio, p7, inBio, signFlags), 1); + ExpectIntGT((smimeSz = BIO_get_mem_data(smimeBio, &smime)), 0); + + /* Run through the various combinations of flags and test against the + * expected results. */ + for (i = 0; i < (int)(sizeof(cases) / sizeof(cases[0])); i++) { + STACK_OF(X509)* certs = (cases[i].certsSel == 1) ? caSk : + (cases[i].certsSel == 2) ? signerSk : NULL; + + ExpectNotNull(readBio = BIO_new_mem_buf(smime, smimeSz)); + ExpectNotNull(p7Ver = SMIME_read_PKCS7(readBio, &content)); + ExpectIntEQ(PKCS7_verify(p7Ver, certs, + cases[i].useStore ? store : NULL, + content, NULL, cases[i].flags), + cases[i].expectOk ? WOLFSSL_SUCCESS : + WC_NO_ERR_TRACE(WOLFSSL_FAILURE)); + + BIO_free(content); + content = NULL; + BIO_free(readBio); + readBio = NULL; + PKCS7_free(p7Ver); + p7Ver = NULL; + } + + /* PKCS7_get0_signers() honors PKCS7_NOINTERN after a verify. */ + ExpectNotNull(readBio = BIO_new_mem_buf(smime, smimeSz)); + ExpectNotNull(p7Ver = SMIME_read_PKCS7(readBio, &content)); + ExpectIntEQ(PKCS7_verify(p7Ver, NULL, NULL, content, NULL, + PKCS7_NOVERIFY), WOLFSSL_SUCCESS); + ExpectNull(PKCS7_get0_signers(p7Ver, caSk, PKCS7_NOINTERN)); + ExpectNull(PKCS7_get0_signers(p7Ver, NULL, PKCS7_NOINTERN)); + ExpectNotNull(signers = PKCS7_get0_signers(p7Ver, signerSk, + PKCS7_NOINTERN)); + ExpectIntEQ(sk_X509_num(signers), 1); + sk_X509_pop_free(signers, X509_free); + + BIO_free(content); + BIO_free(readBio); + PKCS7_free(p7Ver); + PKCS7_free(p7); + BIO_free(smimeBio); + BIO_free(inBio); + sk_X509_free(caSk); + sk_X509_free(signerSk); + X509_STORE_free(store); + X509_free(caCert); + X509_free(signCert); + EVP_PKEY_free(signKey); + BIO_free(keyBio); + BIO_free(certBio); + BIO_free(caBio); +#endif + return EXPECT_RESULT(); +} + /* Testing functions dealing with PKCS12 parsing out X509 certs */ int test_wolfSSL_PKCS12(void) { diff --git a/tests/api/test_ossl_p7p12.h b/tests/api/test_ossl_p7p12.h index 8ab8fd79530..19b45a328bb 100644 --- a/tests/api/test_ossl_p7p12.h +++ b/tests/api/test_ossl_p7p12.h @@ -35,6 +35,7 @@ int test_wolfSSL_PEM_write_bio_PKCS7(void); int test_wolfSSL_PEM_write_bio_encryptedKey(void); int test_wolfSSL_SMIME_read_PKCS7(void); int test_wolfSSL_SMIME_write_PKCS7(void); +int test_wolfSSL_PKCS7_verify_nointern(void); int test_wolfSSL_PKCS12(void); #define TEST_OSSL_PKCS7_DECLS \ @@ -51,7 +52,8 @@ int test_wolfSSL_PKCS12(void); #define TEST_OSSL_SMIME_DECLS \ TEST_DECL_GROUP("ossl_smime", test_wolfSSL_SMIME_read_PKCS7), \ - TEST_DECL_GROUP("ossl_smime", test_wolfSSL_SMIME_write_PKCS7) + TEST_DECL_GROUP("ossl_smime", test_wolfSSL_SMIME_write_PKCS7), \ + TEST_DECL_GROUP("ossl_smime", test_wolfSSL_PKCS7_verify_nointern) #define TEST_OSSL_PKCS12_DECLS \ TEST_DECL_GROUP("ossl_p12", test_wolfSSL_PKCS12)