From 8d4b93d7beae6831f950f3f89c8688f01cd04f34 Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Fri, 7 Oct 2016 12:06:37 +0200 Subject: pkcs7: allow unknown and legacy signature data OIDs to be imported This allows to decode very old PKCS#7 structures where the content is not an octet string. In addition, it introduces gnutls_pkcs7_get_embedded_data_oid() to obtain the OID of the signature data. --- lib/includes/gnutls/pkcs7.h | 3 + lib/libgnutls.map | 1 + lib/pkix.asn | 5 +- lib/pkix_asn1_tab.c | 2 +- lib/x509/pkcs7.c | 186 ++++++++++++++++++++++---------------------- lib/x509/x509_int.h | 4 + 6 files changed, 107 insertions(+), 94 deletions(-) diff --git a/lib/includes/gnutls/pkcs7.h b/lib/includes/gnutls/pkcs7.h index 6d998bc458..59b788bc96 100644 --- a/lib/includes/gnutls/pkcs7.h +++ b/lib/includes/gnutls/pkcs7.h @@ -57,6 +57,9 @@ int gnutls_pkcs7_export2(gnutls_pkcs7_t pkcs7, int gnutls_pkcs7_get_signature_count(gnutls_pkcs7_t pkcs7); int gnutls_pkcs7_get_embedded_data(gnutls_pkcs7_t pkcs7, unsigned idx, gnutls_datum_t *data); +const char * +gnutls_pkcs7_get_embedded_data_oid(gnutls_pkcs7_t pkcs7); + int gnutls_pkcs7_get_crt_count(gnutls_pkcs7_t pkcs7); int gnutls_pkcs7_get_crt_raw(gnutls_pkcs7_t pkcs7, unsigned indx, void *certificate, size_t * certificate_size); diff --git a/lib/libgnutls.map b/lib/libgnutls.map index 9e96fc4a0f..7915563573 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -1107,6 +1107,7 @@ GNUTLS_3_4 gnutls_certificate_set_ocsp_status_request_function2; gnutls_session_ext_register; gnutls_session_supplemental_register; + gnutls_pkcs7_get_embedded_data_oid; local: *; }; diff --git a/lib/pkix.asn b/lib/pkix.asn index 6b6b427a71..20aa7904db 100644 --- a/lib/pkix.asn +++ b/lib/pkix.asn @@ -349,9 +349,12 @@ pkcs-7-SignedData ::= SEQUENCE { pkcs-7-DigestAlgorithmIdentifiers ::= SET OF AlgorithmIdentifier +-- rfc5652: eContent [0] EXPLICIT OCTET STRING OPTIONAL +-- rfc2315: content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL + pkcs-7-EncapsulatedContentInfo ::= SEQUENCE { eContentType pkcs-7-ContentType, - eContent [0] EXPLICIT OCTET STRING OPTIONAL } + eContent [0] EXPLICIT ANY OPTIONAL } -- We don't use CertificateList here since we only want -- to read the raw data. diff --git a/lib/pkix_asn1_tab.c b/lib/pkix_asn1_tab.c index 0bf81125eb..1285284a35 100644 --- a/lib/pkix_asn1_tab.c +++ b/lib/pkix_asn1_tab.c @@ -247,7 +247,7 @@ const asn1_static_node pkix_asn1_tab[] = { { NULL, 2, "AlgorithmIdentifier"}, { "pkcs-7-EncapsulatedContentInfo", 1610612741, NULL }, { "eContentType", 1073741826, "pkcs-7-ContentType"}, - { "eContent", 536895495, NULL }, + { "eContent", 536895501, NULL }, { NULL, 2056, "0"}, { "pkcs-7-CertificateRevocationLists", 1610612751, NULL }, { NULL, 13, NULL }, diff --git a/lib/x509/pkcs7.c b/lib/x509/pkcs7.c index 997b51763a..0d57afa4e7 100644 --- a/lib/x509/pkcs7.c +++ b/lib/x509/pkcs7.c @@ -49,23 +49,22 @@ static const uint8_t one = 1; * which holds them. If raw is non null then the raw decoded * data are copied (they are locally allocated) there. */ -static int _decode_pkcs7_signed_data(ASN1_TYPE pkcs7, ASN1_TYPE * sdata) +static int _decode_pkcs7_signed_data(gnutls_pkcs7_t pkcs7) { - char oid[MAX_OID_SIZE]; ASN1_TYPE c2; - gnutls_datum_t tmp = { NULL, 0 }; int len, result; + gnutls_datum_t tmp = {NULL, 0}; - len = sizeof(oid) - 1; - result = asn1_read_value(pkcs7, "contentType", oid, &len); + len = MAX_OID_SIZE - 1; + result = asn1_read_value(pkcs7->pkcs7, "contentType", pkcs7->encap_data_oid, &len); if (result != ASN1_SUCCESS) { gnutls_assert(); return _gnutls_asn2err(result); } - if (strcmp(oid, SIGNED_DATA_OID) != 0) { + if (strcmp(pkcs7->encap_data_oid, SIGNED_DATA_OID) != 0) { gnutls_assert(); - _gnutls_debug_log("Unknown PKCS7 Content OID '%s'\n", oid); + _gnutls_debug_log("Unknown PKCS7 Content OID '%s'\n", pkcs7->encap_data_oid); return GNUTLS_E_UNKNOWN_PKCS_CONTENT_TYPE; } @@ -79,16 +78,12 @@ static int _decode_pkcs7_signed_data(ASN1_TYPE pkcs7, ASN1_TYPE * sdata) /* the Signed-data has been created, so * decode them. */ - result = _gnutls_x509_read_value(pkcs7, "content", &tmp); + result = _gnutls_x509_read_value(pkcs7->pkcs7, "content", &tmp); if (result < 0) { gnutls_assert(); goto cleanup; } - /* tmp, tmp_size hold the data and the size of the CertificateSet structure - * actually the ANY stuff. - */ - /* Step 1. In case of a signed structure extract certificate set. */ @@ -100,34 +95,66 @@ static int _decode_pkcs7_signed_data(ASN1_TYPE pkcs7, ASN1_TYPE * sdata) } /* read the encapsulated content */ - len = sizeof(oid) - 1; + len = MAX_OID_SIZE - 1; result = - asn1_read_value(c2, "encapContentInfo.eContentType", oid, &len); + asn1_read_value(c2, "encapContentInfo.eContentType", pkcs7->encap_data_oid, &len); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto cleanup; } - if (strcmp(oid, PLAIN_DATA_OID) != 0 - && strcmp(oid, DIGESTED_DATA_OID) != 0) { - gnutls_assert(); + if (strcmp(pkcs7->encap_data_oid, PLAIN_DATA_OID) != 0 + && strcmp(pkcs7->encap_data_oid, DIGESTED_DATA_OID) != 0) { _gnutls_debug_log - ("Unknown or unexpected PKCS7 Encapsulated Content OID '%s'\n", - oid); - result = GNUTLS_E_UNKNOWN_PKCS_CONTENT_TYPE; - goto cleanup; + ("Unknown PKCS#7 Encapsulated Content OID '%s'; treating as raw data\n", + pkcs7->encap_data_oid); + } - *sdata = c2; + /* Try reading as octet string according to rfc5652. If that fails, attempt + * a raw read according to rfc2315 */ + result = _gnutls_x509_read_string(c2, "encapContentInfo.eContent", &pkcs7->der_signed_data, ASN1_ETYPE_OCTET_STRING, 0); + if (result < 0) { + result = _gnutls_x509_read_value(c2, "encapContentInfo.eContent", &pkcs7->der_signed_data); + if (result < 0) { + pkcs7->der_signed_data.data = NULL; + pkcs7->der_signed_data.size = 0; + } else { + int tag_len, len_len; + unsigned char cls; + unsigned long tag; + + /* we skip the embedded element's tag and length - uncharted territorry - used by MICROSOFT_CERT_TRUST_LIST */ + result = asn1_get_tag_der(pkcs7->der_signed_data.data, pkcs7->der_signed_data.size, &cls, &tag_len, &tag); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = asn1_get_length_der(pkcs7->der_signed_data.data+tag_len, pkcs7->der_signed_data.size-tag_len, &len_len); + if (result < 0) { + gnutls_assert(); + result = GNUTLS_E_ASN1_DER_ERROR; + goto cleanup; + } + + tag_len += len_len; + memmove(pkcs7->der_signed_data.data, &pkcs7->der_signed_data.data[tag_len], pkcs7->der_signed_data.size-tag_len); + pkcs7->der_signed_data.size-=tag_len; + } + } + pkcs7->signed_data = c2; gnutls_free(tmp.data); + return 0; cleanup: + gnutls_free(tmp.data); if (c2) asn1_delete_structure(&c2); - gnutls_free(tmp.data); return result; } @@ -192,6 +219,8 @@ void gnutls_pkcs7_deinit(gnutls_pkcs7_t pkcs7) if (pkcs7->signed_data) asn1_delete_structure(&pkcs7->signed_data); + _gnutls_free_datum(&pkcs7->der_signed_data); + gnutls_free(pkcs7); } @@ -256,7 +285,7 @@ gnutls_pkcs7_import(gnutls_pkcs7_t pkcs7, const gnutls_datum_t * data, /* Decode the signed data. */ - result = _decode_pkcs7_signed_data(pkcs7->pkcs7, &pkcs7->signed_data); + result = _decode_pkcs7_signed_data(pkcs7); if (result < 0) { gnutls_assert(); goto cleanup; @@ -725,7 +754,7 @@ int gnutls_pkcs7_get_signature_info(gnutls_pkcs7_t pkcs7, unsigned idx, * and matches our calculated hash */ static int verify_hash_attr(gnutls_pkcs7_t pkcs7, const char *root, gnutls_sign_algorithm_t algo, - const gnutls_datum_t * data) + const gnutls_datum_t *data) { unsigned hash; gnutls_datum_t tmp = { NULL, 0 }; @@ -747,23 +776,10 @@ static int verify_hash_attr(gnutls_pkcs7_t pkcs7, const char *root, hash_size = gnutls_hash_get_len(hash); if (data == NULL || data->data == NULL) { - ret = - _gnutls_x509_read_value(pkcs7->signed_data, - "encapContentInfo.eContent", &tmp); - if (ret < 0) { - if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) - ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; - gnutls_assert(); - return ret; - } - data = &tmp; + data = &pkcs7->der_signed_data; } ret = gnutls_hash_fast(hash, data->data, data->size, hash_output); - - gnutls_free(tmp.data); - tmp.data = NULL; - if (ret < 0) return gnutls_assert_val(ret); @@ -794,6 +810,8 @@ static int verify_hash_attr(gnutls_pkcs7_t pkcs7, const char *root, if (tmp2.size == hash_size && memcmp(hash_output, tmp2.data, tmp2.size) == 0) { msg_digest_ok = 1; + } else { + gnutls_assert(); } } else if (strcmp(oid, ATTR_CONTENT_TYPE) == 0) { if (num_cont_types > 0) { @@ -821,6 +839,7 @@ static int verify_hash_attr(gnutls_pkcs7_t pkcs7, const char *root, goto cleanup; } } + gnutls_free(tmp.data); tmp.data = NULL; gnutls_free(tmp2.data); @@ -866,15 +885,7 @@ static int figure_pkcs7_sigdata(gnutls_pkcs7_t pkcs7, const char *root, /* We have no signedAttrs. Use the provided data, or the encapsulated */ if (data == NULL || data->data == NULL) { - ret = - _gnutls_x509_read_value(pkcs7->signed_data, - "encapContentInfo.eContent", - sigdata); - if (ret < 0) { - gnutls_assert(); - return gnutls_assert_val(ret); - } - return 0; + return _gnutls_set_datum(sigdata, pkcs7->der_signed_data.data, pkcs7->der_signed_data.size); } return _gnutls_set_datum(sigdata, data->data, data->size); @@ -883,7 +894,7 @@ static int figure_pkcs7_sigdata(gnutls_pkcs7_t pkcs7, const char *root, /** * gnutls_pkcs7_get_embedded_data: * @pkcs7: should contain a gnutls_pkcs7_t type - * @idx: the index of the signature info to get the data from + * @flags: must be zero * @data: will hold the embedded data in the provided structure * * This function will return the data embedded in the signature of @@ -900,46 +911,38 @@ static int figure_pkcs7_sigdata(gnutls_pkcs7_t pkcs7, const char *root, * Since: 3.4.8 **/ int -gnutls_pkcs7_get_embedded_data(gnutls_pkcs7_t pkcs7, unsigned idx, - gnutls_datum_t * data) +gnutls_pkcs7_get_embedded_data(gnutls_pkcs7_t pkcs7, unsigned flags, + gnutls_datum_t *data) { - int count, ret; - gnutls_datum_t tmpdata = { NULL, 0 }; - gnutls_pkcs7_signature_info_st info; - char root[128]; - - memset(&info, 0, sizeof(info)); - if (pkcs7 == NULL) return GNUTLS_E_INVALID_REQUEST; - ret = - asn1_number_of_elements(pkcs7->signed_data, "signerInfos", &count); - if (ret != ASN1_SUCCESS || idx + 1 > (unsigned)count) { - gnutls_assert(); - return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; - } - - ret = gnutls_pkcs7_get_signature_info(pkcs7, idx, &info); - if (ret < 0) { - gnutls_assert(); - goto cleanup; - } - - snprintf(root, sizeof(root), "signerInfos.?%u", idx + 1); - ret = figure_pkcs7_sigdata(pkcs7, root, NULL, info.algo, data); - if (ret < 0) { - gnutls_assert(); - goto cleanup; - } + if (pkcs7->der_signed_data.size == 0) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); - ret = 0; + return _gnutls_set_datum(data, pkcs7->der_signed_data.data, pkcs7->der_signed_data.size); +} - cleanup: - gnutls_free(tmpdata.data); - gnutls_pkcs7_signature_info_deinit(&info); +/** + * gnutls_pkcs7_get_embedded_data_oid: + * @pkcs7: should contain a gnutls_pkcs7_t type + * + * This function will return the OID of the data embedded in the signature of + * the PKCS7 structure. If no data are available then %NULL will be + * returned. The returned value will be valid during the lifetime + * of the @pkcs7 structure. + * + * Returns: On success, a pointer to an OID string, %NULL on error. + * + * Since: 3.5.5 + **/ +const char * +gnutls_pkcs7_get_embedded_data_oid(gnutls_pkcs7_t pkcs7) +{ + if (pkcs7 == NULL || pkcs7->encap_data_oid[0] == 0) + return NULL; - return ret; + return pkcs7->encap_data_oid; } /** @@ -965,7 +968,7 @@ gnutls_pkcs7_get_embedded_data(gnutls_pkcs7_t pkcs7, unsigned idx, int gnutls_pkcs7_verify_direct(gnutls_pkcs7_t pkcs7, gnutls_x509_crt_t signer, unsigned idx, - const gnutls_datum_t * data, unsigned flags) + const gnutls_datum_t *data, unsigned flags) { int count, ret; gnutls_datum_t tmpdata = { NULL, 0 }; @@ -1188,10 +1191,10 @@ gnutls_x509_crt_t find_signer(gnutls_pkcs7_t pkcs7, gnutls_x509_trust_list_t tl, **/ int gnutls_pkcs7_verify(gnutls_pkcs7_t pkcs7, gnutls_x509_trust_list_t tl, - gnutls_typed_vdata_st * vdata, + gnutls_typed_vdata_st *vdata, unsigned int vdata_size, unsigned idx, - const gnutls_datum_t * data, unsigned flags) + const gnutls_datum_t *data, unsigned flags) { int count, ret; gnutls_datum_t tmpdata = { NULL, 0 }; @@ -2117,7 +2120,7 @@ static int write_attributes(ASN1_TYPE c2, const char *root, int gnutls_pkcs7_sign(gnutls_pkcs7_t pkcs7, gnutls_x509_crt_t signer, gnutls_privkey_t signer_key, - const gnutls_datum_t * data, + const gnutls_datum_t *data, gnutls_pkcs7_attrs_t signed_attrs, gnutls_pkcs7_attrs_t unsigned_attrs, gnutls_digest_algorithm_t dig, unsigned flags) @@ -2160,12 +2163,11 @@ int gnutls_pkcs7_sign(gnutls_pkcs7_t pkcs7, } if (flags & GNUTLS_PKCS7_EMBED_DATA && data->data) { /* embed data */ - result = - asn1_write_value(pkcs7->signed_data, - "encapContentInfo.eContent", data->data, - data->size); - if (result != ASN1_SUCCESS) { - ret = _gnutls_asn2err(result); + ret = + _gnutls_x509_write_string(pkcs7->signed_data, + "encapContentInfo.eContent", data, + ASN1_ETYPE_OCTET_STRING); + if (ret < 0) { goto cleanup; } } diff --git a/lib/x509/x509_int.h b/lib/x509/x509_int.h index 906ec81bae..b64ee31d9d 100644 --- a/lib/x509/x509_int.h +++ b/lib/x509/x509_int.h @@ -105,6 +105,10 @@ typedef struct gnutls_pkcs7_attrs_st { typedef struct gnutls_pkcs7_int { ASN1_TYPE pkcs7; + + char encap_data_oid[MAX_OID_SIZE]; + + gnutls_datum_t der_signed_data; ASN1_TYPE signed_data; unsigned expanded; } gnutls_pkcs7_int; -- cgit v1.2.1