summaryrefslogtreecommitdiffstats
path: root/src/crypto/x509/pkcs7.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/crypto/x509/pkcs7.c')
-rw-r--r--src/crypto/x509/pkcs7.c188
1 files changed, 169 insertions, 19 deletions
diff --git a/src/crypto/x509/pkcs7.c b/src/crypto/x509/pkcs7.c
index 75c101b..9a4e490 100644
--- a/src/crypto/x509/pkcs7.c
+++ b/src/crypto/x509/pkcs7.c
@@ -14,6 +14,8 @@
#include <openssl/x509.h>
+#include <assert.h>
+
#include <openssl/bytestring.h>
#include <openssl/err.h>
#include <openssl/obj.h>
@@ -22,21 +24,26 @@
#include "../bytestring/internal.h"
-int PKCS7_get_certificates(STACK_OF(X509) *out_certs, CBS *cbs) {
- uint8_t *der_bytes = NULL;
+/* pkcs7_parse_header reads the non-certificate/non-CRL prefix of a PKCS#7
+ * SignedData blob from |cbs| and sets |*out| to point to the rest of the
+ * input. If the input is in BER format, then |*der_bytes| will be set to a
+ * pointer that needs to be freed by the caller once they have finished
+ * processing |*out| (which will be pointing into |*der_bytes|).
+ *
+ * It returns one on success or zero on error. On error, |*der_bytes| is
+ * NULL. */
+static int pkcs7_parse_header(uint8_t **der_bytes, CBS *out, CBS *cbs) {
size_t der_len;
- CBS in, content_info, content_type, wrapped_signed_data, signed_data,
- certificates;
- const size_t initial_certs_len = sk_X509_num(out_certs);
+ CBS in, content_info, content_type, wrapped_signed_data, signed_data;
uint64_t version;
- int ret = 0;
/* The input may be in BER format. */
- if (!CBS_asn1_ber_to_der(cbs, &der_bytes, &der_len)) {
+ *der_bytes = NULL;
+ if (!CBS_asn1_ber_to_der(cbs, der_bytes, &der_len)) {
return 0;
}
- if (der_bytes != NULL) {
- CBS_init(&in, der_bytes, der_len);
+ if (*der_bytes != NULL) {
+ CBS_init(&in, *der_bytes, der_len);
} else {
CBS_init(&in, CBS_data(cbs), CBS_len(cbs));
}
@@ -48,7 +55,7 @@ int PKCS7_get_certificates(STACK_OF(X509) *out_certs, CBS *cbs) {
}
if (OBJ_cbs2nid(&content_type) != NID_pkcs7_signed) {
- OPENSSL_PUT_ERROR(X509, PKCS7_get_certificates,
+ OPENSSL_PUT_ERROR(X509, pkcs7_parse_header,
X509_R_NOT_PKCS7_SIGNED_DATA);
goto err;
}
@@ -64,11 +71,34 @@ int PKCS7_get_certificates(STACK_OF(X509) *out_certs, CBS *cbs) {
}
if (version < 1) {
- OPENSSL_PUT_ERROR(X509, PKCS7_get_certificates,
+ OPENSSL_PUT_ERROR(X509, pkcs7_parse_header,
X509_R_BAD_PKCS7_VERSION);
goto err;
}
+ CBS_init(out, CBS_data(&signed_data), CBS_len(&signed_data));
+ return 1;
+
+err:
+ if (*der_bytes) {
+ OPENSSL_free(*der_bytes);
+ *der_bytes = NULL;
+ }
+
+ return 0;
+}
+
+int PKCS7_get_certificates(STACK_OF(X509) *out_certs, CBS *cbs) {
+ CBS signed_data, certificates;
+ uint8_t *der_bytes = NULL;
+ int ret = 0;
+ const size_t initial_certs_len = sk_X509_num(out_certs);
+
+ if (!pkcs7_parse_header(&der_bytes, &signed_data, cbs)) {
+ return 0;
+ }
+
+ /* See https://tools.ietf.org/html/rfc2315#section-9.1 */
if (!CBS_get_asn1(&signed_data, &certificates,
CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0)) {
OPENSSL_PUT_ERROR(X509, PKCS7_get_certificates,
@@ -91,12 +121,12 @@ int PKCS7_get_certificates(STACK_OF(X509) *out_certs, CBS *cbs) {
goto err;
}
- if (inp != CBS_data(&cert) + CBS_len(&cert)) {
- /* This suggests a disconnect between the two ASN.1 parsers. */
+ assert(inp == CBS_data(&cert) + CBS_len(&cert));
+
+ if (sk_X509_push(out_certs, x509) == 0) {
+ X509_free(x509);
goto err;
}
-
- sk_X509_push(out_certs, x509);
}
ret = 1;
@@ -116,10 +146,83 @@ err:
return ret;
}
-int PKCS7_bundle_certificates(CBB *out, const STACK_OF(X509) *certs) {
+int PKCS7_get_CRLs(STACK_OF(X509_CRL) *out_crls, CBS *cbs) {
+ CBS signed_data, crls;
+ uint8_t *der_bytes = NULL;
+ int ret = 0;
+ const size_t initial_crls_len = sk_X509_CRL_num(out_crls);
+
+ if (!pkcs7_parse_header(&der_bytes, &signed_data, cbs)) {
+ return 0;
+ }
+
+ /* See https://tools.ietf.org/html/rfc2315#section-9.1 */
+
+ /* Even if only CRLs are included, there may be an empty certificates block.
+ * OpenSSL does this, for example. */
+ if (CBS_peek_asn1_tag(&signed_data,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) &&
+ !CBS_get_asn1(&signed_data, NULL /* certificates */,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0)) {
+ goto err;
+ }
+
+ if (!CBS_get_asn1(&signed_data, &crls,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1)) {
+ OPENSSL_PUT_ERROR(X509, PKCS7_get_CRLs,
+ X509_R_NO_CRLS_INCLUDED);
+ goto err;
+ }
+
+ while (CBS_len(&crls) > 0) {
+ CBS crl_data;
+ X509_CRL *crl;
+ const uint8_t *inp;
+
+ if (!CBS_get_asn1_element(&crls, &crl_data, CBS_ASN1_SEQUENCE)) {
+ goto err;
+ }
+
+ inp = CBS_data(&crl_data);
+ crl = d2i_X509_CRL(NULL, &inp, CBS_len(&crl_data));
+ if (!crl) {
+ goto err;
+ }
+
+ assert(inp == CBS_data(&crl_data) + CBS_len(&crl_data));
+
+ if (sk_X509_CRL_push(out_crls, crl) == 0) {
+ X509_CRL_free(crl);
+ goto err;
+ }
+ }
+
+ ret = 1;
+
+err:
+ if (der_bytes) {
+ OPENSSL_free(der_bytes);
+ }
+
+ if (!ret) {
+ while (sk_X509_CRL_num(out_crls) != initial_crls_len) {
+ X509_CRL_free(sk_X509_CRL_pop(out_crls));
+ }
+ }
+
+ 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
+ * error.
+ *
+ * pkcs7_bundle returns one on success or zero on error. */
+static int pkcs7_bundle(CBB *out, int (*cb)(CBB *out, const void *arg),
+ const void *arg) {
CBB outer_seq, wrapped_seq, seq, version_bytes, digest_algos_set,
- content_info, certificates;
- size_t i;
+ content_info;
/* See https://tools.ietf.org/html/rfc2315#section-7 */
if (!CBB_add_asn1(out, &outer_seq, CBS_ASN1_SEQUENCE) ||
@@ -133,7 +236,20 @@ int PKCS7_bundle_certificates(CBB *out, const STACK_OF(X509) *certs) {
!CBB_add_asn1(&seq, &digest_algos_set, CBS_ASN1_SET) ||
!CBB_add_asn1(&seq, &content_info, CBS_ASN1_SEQUENCE) ||
!OBJ_nid2cbb(&content_info, NID_pkcs7_data) ||
- !CBB_add_asn1(&seq, &certificates,
+ !cb(&seq, arg)) {
+ return 0;
+ }
+
+ return CBB_flush(out);
+}
+
+static int pkcs7_bundle_certificates_cb(CBB *out, const void *arg) {
+ const STACK_OF(X509) *certs = arg;
+ size_t i;
+ CBB certificates;
+
+ /* See https://tools.ietf.org/html/rfc2315#section-9.1 */
+ if (!CBB_add_asn1(out, &certificates,
CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0)) {
return 0;
}
@@ -152,3 +268,37 @@ int PKCS7_bundle_certificates(CBB *out, const STACK_OF(X509) *certs) {
return CBB_flush(out);
}
+
+int PKCS7_bundle_certificates(CBB *out, const STACK_OF(X509) *certs) {
+ return pkcs7_bundle(out, pkcs7_bundle_certificates_cb, certs);
+}
+
+static int pkcs7_bundle_crls_cb(CBB *out, const void *arg) {
+ const STACK_OF(X509_CRL) *crls = arg;
+ size_t i;
+ CBB crl_data;
+
+ /* See https://tools.ietf.org/html/rfc2315#section-9.1 */
+ if (!CBB_add_asn1(out, &crl_data,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1)) {
+ return 0;
+ }
+
+ for (i = 0; i < sk_X509_CRL_num(crls); i++) {
+ X509_CRL *crl = sk_X509_CRL_value(crls, i);
+ uint8_t *buf;
+ int len = i2d_X509_CRL(crl, NULL);
+
+ if (len < 0 ||
+ !CBB_add_space(&crl_data, &buf, len) ||
+ i2d_X509_CRL(crl, &buf) < 0) {
+ return 0;
+ }
+ }
+
+ return CBB_flush(out);
+}
+
+int PKCS7_bundle_CRLs(CBB *out, const STACK_OF(X509_CRL) *crls) {
+ return pkcs7_bundle(out, pkcs7_bundle_crls_cb, crls);
+}