Add support for reading PKCS#7 data from PEM files.

(There are times when I actually miss C++ templates.)

Change-Id: I3db56e4946ae4fb919105fa33e2cfce3c7542d37
Reviewed-on: https://boringssl-review.googlesource.com/3700
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/x509/pkcs7.c b/crypto/x509/pkcs7.c
index 9a4e490..bb86077 100644
--- a/crypto/x509/pkcs7.c
+++ b/crypto/x509/pkcs7.c
@@ -19,6 +19,7 @@
 #include <openssl/bytestring.h>
 #include <openssl/err.h>
 #include <openssl/obj.h>
+#include <openssl/pem.h>
 #include <openssl/stack.h>
 
 #include "../bytestring/internal.h"
@@ -213,6 +214,50 @@
   return ret;
 }
 
+int PKCS7_get_PEM_certificates(STACK_OF(X509) *out_certs, BIO *pem_bio) {
+  uint8_t *data;
+  long len;
+  int ret;
+
+  /* Even though we pass PEM_STRING_PKCS7 as the expected PEM type here, PEM
+   * internally will actually allow several other values too, including
+   * "CERTIFICATE". */
+  if (!PEM_bytes_read_bio(&data, &len, NULL /* PEM type output */,
+                          PEM_STRING_PKCS7, pem_bio,
+                          NULL /* password callback */,
+                          NULL /* password callback argument */)) {
+    return 0;
+  }
+
+  CBS cbs;
+  CBS_init(&cbs, data, len);
+  ret = PKCS7_get_certificates(out_certs, &cbs);
+  OPENSSL_free(data);
+  return ret;
+}
+
+int PKCS7_get_PEM_CRLs(STACK_OF(X509_CRL) *out_crls, BIO *pem_bio) {
+  uint8_t *data;
+  long len;
+  int ret;
+
+  /* Even though we pass PEM_STRING_PKCS7 as the expected PEM type here, PEM
+   * internally will actually allow several other values too, including
+   * "CERTIFICATE". */
+  if (!PEM_bytes_read_bio(&data, &len, NULL /* PEM type output */,
+                          PEM_STRING_PKCS7, pem_bio,
+                          NULL /* password callback */,
+                          NULL /* password callback argument */)) {
+    return 0;
+  }
+
+  CBS cbs;
+  CBS_init(&cbs, data, len);
+  ret = PKCS7_get_CRLs(out_crls, &cbs);
+  OPENSSL_free(data);
+  return ret;
+}
+
 /* pkcs7_bundle writes a PKCS#7, SignedData structure to |out| and then calls
  * |cb| with a CBB to which certificate or CRL data can be written, and the
  * opaque context pointer, |arg|. The callback can return zero to indicate an
diff --git a/crypto/x509/pkcs7_test.c b/crypto/x509/pkcs7_test.c
index ff61a2b..bac9fb2 100644
--- a/crypto/x509/pkcs7_test.c
+++ b/crypto/x509/pkcs7_test.c
@@ -412,6 +412,60 @@
     0xf0, 0x00, 0x54, 0x31, 0x00,
 };
 
+/* kPEMCert is the result of exporting the mail.google.com certificate from
+ * Chrome and then running it through:
+ *   openssl pkcs7 -inform DER -in mail.google.com -outform PEM */
+static const char kPEMCert[] =
+    "-----BEGIN PKCS7-----\n"
+    "MIID+wYJKoZIhvcNAQcCoIID7DCCA+gCAQExADALBgkqhkiG9w0BBwGgggPQMIID\n"
+    "zDCCArSgAwIBAgIIWesoywKxoNQwDQYJKoZIhvcNAQELBQAwSTELMAkGA1UEBhMC\n"
+    "VVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2dsZSBJbnRlcm5l\n"
+    "dCBBdXRob3JpdHkgRzIwHhcNMTUwMjExMTQxNTA2WhcNMTUwNTEyMDAwMDAwWjBp\n"
+    "MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91\n"
+    "bnRhaW4gVmlldzETMBEGA1UECgwKR29vZ2xlIEluYzEYMBYGA1UEAwwPbWFpbC5n\n"
+    "b29nbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7MdALmCkcRRf/tzQ\n"
+    "a8eu3J7S5CTQa5ns0ReF9ktlbB1RL56BVGAu4p7BrT32D6gDpiggXq3gxN81A0TG\n"
+    "C2yICKOCAWEwggFdMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAsBgNV\n"
+    "HREEJTAjgg9tYWlsLmdvb2dsZS5jb22CEGluYm94Lmdvb2dsZS5jb20wCwYDVR0P\n"
+    "BAQDAgeAMGgGCCsGAQUFBwEBBFwwWjArBggrBgEFBQcwAoYfaHR0cDovL3BraS5n\n"
+    "b29nbGUuY29tL0dJQUcyLmNydDArBggrBgEFBQcwAYYfaHR0cDovL2NsaWVudHMx\n"
+    "Lmdvb2dsZS5jb20vb2NzcDAdBgNVHQ4EFgQUQqsYsRoWLiG6qmV2N1mpYaHawxAw\n"
+    "DAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBRK3QYWG7z2aLV29YG2u2IaulqBLzAX\n"
+    "BgNVHSAEEDAOMAwGCisGAQQB1nkCBQEwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDov\n"
+    "L3BraS5nb29nbGUuY29tL0dJQUcyLmNybDANBgkqhkiG9w0BAQsFAAOCAQEAKNh3\n"
+    "isNuGBisPKVlekOsZR6S8oP/fS/xt6Hqvg0EwFXvhxoJ40rxAB2LMykY17e+ln3P\n"
+    "MwBBlRkwY1btcDT15JwzgaZb38rq/r+Pkb5Qgmx/InA/pw0QHDtwHQp5uXZuvu6p\n"
+    "J/SlCwyq7EOvByWdVQcMU/dhGa3idXEkn/zwfqcG6YjdWKoDmXWZYv3RiP3wJcRB\n"
+    "9+3U1wOe3uebnZLRWO6/w0to1XY8TFHklyw5rwIE5sbxOx5N3Ne8+GgPrUDvGAz0\n"
+    "rAUKnh3b7GNXL1qlZh2qkhB6rUzvtPpg397Asg3xVtExCHOk4zPqzzicttoEbVVy\n"
+    "0T8rIMUNwC4Beh4JVjEA\n"
+    "-----END PKCS7-----\n";
+
+/* kPEMCRL is the result of downloading the Equifax CRL and running:
+     openssl crl2pkcs7 -inform DER -in secureca.crl  */
+static const char kPEMCRL[] =
+    "-----BEGIN PKCS7-----\n"
+    "MIIDhQYJKoZIhvcNAQcCoIIDdjCCA3ICAQExADALBgkqhkiG9w0BBwGgAKGCA1gw\n"
+    "ggNUMIICvTANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEQMA4GA1UEChMH\n"
+    "RXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0\n"
+    "aG9yaXR5Fw0xNTAyMjcwMTIzMDBaFw0xNTAzMDkwMTIzMDBaMIICPDAUAgMPWOQX\n"
+    "DTE0MDQyNzA4MTkyMlowFAIDFHYZFw0xNDA2MTgxNTAwMDNaMBQCAw+a+xcNMTQw\n"
+    "NDI5MTgwOTE3WjAUAgMUi8AXDTE0MDcwOTE5NDYzM1owFAIDFOScFw0xNDA0MTYy\n"
+    "MzM5MzVaMBQCAw+GBxcNMTQwNTIxMTU1MDUzWjAUAgMS4ikXDTE0MDYxNzE4NTUx\n"
+    "NVowFAIDDUJmFw0xMjA2MjcxNzEwNTNaMBQCAwMeMxcNMDIwNTE1MTMwNjExWjAU\n"
+    "AgMS4iMXDTE0MDYwNjIwNDAyMVowFAIDE5yrFw0xMDA3MjkxNjQ0MzlaMBQCAxLG\n"
+    "ChcNMTQwNjA2MjIyMTM5WjAUAgMDJYUXDTAyMDUxNDE4MTE1N1owFAIDFIbmFw0x\n"
+    "NDA3MjUwMjAwMzhaMBQCAxOcoRcNMTAwNzI5MTY0NzMyWjAUAgMVTVwXDTE0MDQz\n"
+    "MDAwMDQ0MlowFAIDD/otFw0xNDA2MTcxODUwMTFaMBQCAxN1VRcNMTUwMTE4MDIy\n"
+    "MTMzWjAUAgMPVpYXDTE0MDYyNDEyMzEwMlowFAIDC4CKFw0xMjA2MjcxNzEwMjVa\n"
+    "MBQCAw+UFhcNMTAwMzAxMTM0NTMxWjAUAgMUFrMXDTE0MDYxODE0MzI1NlowFAID\n"
+    "CuGFFw0xMjA2MjcxNzEwMTdaMBQCAxTMPhcNMTQwNzExMTI1NTMxWjAUAgMQW8sX\n"
+    "DTEwMDczMDIxMzEyMFowFAIDFWofFw0xNDAyMjYxMjM1MTlaMA0GCSqGSIb3DQEB\n"
+    "BQUAA4GBAB1cJwcRA/IAvfRGPnH9EISD2dLSGaAg9xpDPazaM/y3QmAapKiyB1xR\n"
+    "FsBCgAoP8EdbS3iQr8esSPjKPBNe9tGIrlWjDIpiRyn4crgkF6+yBh6ncnarlh3g\n"
+    "fNQMQoI9So4Vdy88Kow6BBBV3Lu6sZHue+cjxXETrmshNdNk8ABUMQA=\n"
+    "-----END PKCS7-----\n";
+
 static int test_cert_reparse(const uint8_t *der_bytes, size_t der_len) {
   CBS pkcs7;
   CBB cbb;
@@ -540,12 +594,58 @@
   return 1;
 }
 
+static int test_pem_certs(const char *pem) {
+  BIO *bio = BIO_new_mem_buf((char *) pem, strlen(pem));
+  STACK_OF(X509) *certs = sk_X509_new_null();
+
+  if (!PKCS7_get_PEM_certificates(certs, bio)) {
+    fprintf(stderr, "PKCS7_get_PEM_certificates failed.\n");
+    return 0;
+  }
+
+  if (sk_X509_num(certs) != 1) {
+    fprintf(stderr,
+            "Bad number of certificates from PKCS7_get_PEM_certificates: %u\n",
+            (unsigned)sk_X509_num(certs));
+    return 0;
+  }
+
+  BIO_free(bio);
+  sk_X509_pop_free(certs, X509_free);
+
+  return 1;
+}
+
+static int test_pem_crls(const char *pem) {
+  BIO *bio = BIO_new_mem_buf((char *) pem, strlen(pem));
+  STACK_OF(X509_CRL) *crls = sk_X509_CRL_new_null();
+
+  if (!PKCS7_get_PEM_CRLs(crls, bio)) {
+    fprintf(stderr, "PKCS7_get_PEM_CRLs failed.\n");
+    return 0;
+  }
+
+  if (sk_X509_CRL_num(crls) != 1) {
+    fprintf(stderr,
+            "Bad number of CRLs from PKCS7_get_PEM_CRLs: %u\n",
+            (unsigned)sk_X509_CRL_num(crls));
+    return 0;
+  }
+
+  BIO_free(bio);
+  sk_X509_CRL_pop_free(crls, X509_CRL_free);
+
+  return 1;
+}
+
 int main(void) {
   CRYPTO_library_init();
 
   if (!test_cert_reparse(kPKCS7NSS, sizeof(kPKCS7NSS)) ||
       !test_cert_reparse(kPKCS7Windows, sizeof(kPKCS7Windows)) ||
-      !test_crl_reparse(kOpenSSLCRL, sizeof(kOpenSSLCRL))) {
+      !test_crl_reparse(kOpenSSLCRL, sizeof(kOpenSSLCRL)) ||
+      !test_pem_certs(kPEMCert) ||
+      !test_pem_crls(kPEMCRL)) {
     return 1;
   }
 
diff --git a/include/openssl/x509.h b/include/openssl/x509.h
index 4025cf9..ef1d7fb 100644
--- a/include/openssl/x509.h
+++ b/include/openssl/x509.h
@@ -1176,6 +1176,17 @@
  * |crls| to |out|. It returns one on success and zero on error. */
 OPENSSL_EXPORT int PKCS7_bundle_CRLs(CBB *out, const STACK_OF(X509_CRL) *crls);
 
+/* PKCS7_get_PEM_certificates reads a PEM-encoded, PKCS#7, SignedData structure
+ * from |pem_bio| and appends the included certificates to |out_certs|. It
+ * returns one on success and zero on error. */
+OPENSSL_EXPORT int PKCS7_get_PEM_certificates(STACK_OF(X509) *out_certs,
+                                              BIO *pem_bio);
+
+/* PKCS7_get_PEM_CRLs reads a PEM-encoded, PKCS#7, SignedData structure from
+ * |pem_bio| and appends the included CRLs to |out_crls|. It returns one on
+ * success and zero on error. */
+OPENSSL_EXPORT int PKCS7_get_PEM_CRLs(STACK_OF(X509_CRL) *out_crls,
+                                      BIO *pem_bio);
 
 /* EVP_PK values indicate the algorithm of the public key in a certificate. */