xref: /openbsd-src/usr.sbin/rpki-client/cms.c (revision a0b977827c761d8227fbe3802ea7b395a998ddf6)
1*a0b97782Stb /*	$OpenBSD: cms.c,v 1.50 2024/11/27 15:19:26 tb Exp $ */
29a7e9e7fSjob /*
39a7e9e7fSjob  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
49a7e9e7fSjob  *
59a7e9e7fSjob  * Permission to use, copy, modify, and distribute this software for any
69a7e9e7fSjob  * purpose with or without fee is hereby granted, provided that the above
79a7e9e7fSjob  * copyright notice and this permission notice appear in all copies.
89a7e9e7fSjob  *
99a7e9e7fSjob  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
109a7e9e7fSjob  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
119a7e9e7fSjob  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
129a7e9e7fSjob  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
139a7e9e7fSjob  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
149a7e9e7fSjob  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
159a7e9e7fSjob  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
169a7e9e7fSjob  */
179a7e9e7fSjob 
189a7e9e7fSjob #include <err.h>
199a7e9e7fSjob #include <stdint.h>
209a7e9e7fSjob #include <stdlib.h>
219a7e9e7fSjob #include <string.h>
229a7e9e7fSjob #include <unistd.h>
239a7e9e7fSjob 
24ef3f6f56Sjob #include <openssl/bio.h>
259a7e9e7fSjob #include <openssl/cms.h>
269a7e9e7fSjob 
279a7e9e7fSjob #include "extern.h"
289a7e9e7fSjob 
299025e295Sclaudio extern ASN1_OBJECT	*cnt_type_oid;
309025e295Sclaudio extern ASN1_OBJECT	*msg_dgst_oid;
319025e295Sclaudio extern ASN1_OBJECT	*sign_time_oid;
329025e295Sclaudio 
33ef3f6f56Sjob static int
34f8cba460Stb cms_extract_econtent(const char *fn, CMS_ContentInfo *cms, unsigned char **res,
35f8cba460Stb     size_t *rsz)
36f8cba460Stb {
37f8cba460Stb 	ASN1_OCTET_STRING		**os = NULL;
38f8cba460Stb 
39f8cba460Stb 	/* Detached signature case: no eContent to extract, so do nothing. */
40f8cba460Stb 	if (res == NULL || rsz == NULL)
41f8cba460Stb 		return 1;
42f8cba460Stb 
43f8cba460Stb 	if ((os = CMS_get0_content(cms)) == NULL || *os == NULL) {
44f8cba460Stb 		warnx("%s: RFC 6488 section 2.1.4: "
45f8cba460Stb 		    "eContent: zero-length content", fn);
46f8cba460Stb 		return 0;
47f8cba460Stb 	}
48f8cba460Stb 
49f8cba460Stb 	/*
50f8cba460Stb 	 * Extract and duplicate the eContent.
51f8cba460Stb 	 * The CMS framework offers us no other way of easily managing
52f8cba460Stb 	 * this information; and since we're going to d2i it anyway,
53f8cba460Stb 	 * simply pass it as the desired underlying types.
54f8cba460Stb 	 */
55f8cba460Stb 	if ((*res = malloc((*os)->length)) == NULL)
56f8cba460Stb 		err(1, NULL);
57f8cba460Stb 	memcpy(*res, (*os)->data, (*os)->length);
58f8cba460Stb 	*rsz = (*os)->length;
59f8cba460Stb 
60f8cba460Stb 	return 1;
61f8cba460Stb }
62f8cba460Stb 
63f8cba460Stb static int
641bb1e509Sjob cms_get_signtime(const char *fn, X509_ATTRIBUTE *attr, time_t *signtime)
651bb1e509Sjob {
661bb1e509Sjob 	const ASN1_TIME		*at;
671bb1e509Sjob 	const char		*time_str = "UTCtime";
681bb1e509Sjob 	int			 time_type = V_ASN1_UTCTIME;
691bb1e509Sjob 
7081c43103Stb 	*signtime = 0;
711bb1e509Sjob 	at = X509_ATTRIBUTE_get0_data(attr, 0, time_type, NULL);
721bb1e509Sjob 	if (at == NULL) {
731bb1e509Sjob 		time_str = "GeneralizedTime";
741bb1e509Sjob 		time_type = V_ASN1_GENERALIZEDTIME;
751bb1e509Sjob 		at = X509_ATTRIBUTE_get0_data(attr, 0, time_type, NULL);
761bb1e509Sjob 		if (at == NULL) {
771bb1e509Sjob 			warnx("%s: CMS signing-time issue", fn);
781bb1e509Sjob 			return 0;
791bb1e509Sjob 		}
801bb1e509Sjob 		warnx("%s: GeneralizedTime instead of UTCtime", fn);
811bb1e509Sjob 	}
821bb1e509Sjob 
831bb1e509Sjob 	if (!x509_get_time(at, signtime)) {
841bb1e509Sjob 		warnx("%s: failed to convert %s", fn, time_str);
851bb1e509Sjob 		return 0;
861bb1e509Sjob 	}
871bb1e509Sjob 
881bb1e509Sjob 	return 1;
891bb1e509Sjob }
901bb1e509Sjob 
911bb1e509Sjob static int
92ef3f6f56Sjob cms_parse_validate_internal(X509 **xp, const char *fn, const unsigned char *der,
93797cceeeStb     size_t len, const ASN1_OBJECT *oid, BIO *bio, unsigned char **res,
941bb1e509Sjob     size_t *rsz, time_t *signtime)
959a7e9e7fSjob {
96797cceeeStb 	const unsigned char		*oder;
979025e295Sclaudio 	char				 buf[128], obuf[128];
989025e295Sclaudio 	const ASN1_OBJECT		*obj, *octype;
99f8cba460Stb 	ASN1_OCTET_STRING		*kid = NULL;
1009a7e9e7fSjob 	CMS_ContentInfo			*cms;
101de232e1dStb 	long				 version;
1029a7e9e7fSjob 	STACK_OF(X509)			*certs = NULL;
103475ff837Sjob 	STACK_OF(X509_CRL)		*crls = NULL;
1049025e295Sclaudio 	STACK_OF(CMS_SignerInfo)	*sinfos;
1059025e295Sclaudio 	CMS_SignerInfo			*si;
106ae36eebeSjob 	EVP_PKEY			*pkey;
1079025e295Sclaudio 	X509_ALGOR			*pdig, *psig;
1089025e295Sclaudio 	int				 i, nattrs, nid;
109968e2494Sjob 	int				 has_ct = 0, has_md = 0, has_st = 0;
110db13aa11Sjob 	time_t				 notafter;
111ef3f6f56Sjob 	int				 rc = 0;
1129a7e9e7fSjob 
1139a7e9e7fSjob 	*xp = NULL;
114ef3f6f56Sjob 	if (rsz != NULL)
115ef3f6f56Sjob 		*rsz = 0;
11681c43103Stb 	*signtime = 0;
1179a7e9e7fSjob 
118cabf3a3bSclaudio 	/* just fail for empty buffers, the warning was printed elsewhere */
119cabf3a3bSclaudio 	if (der == NULL)
120ef3f6f56Sjob 		return 0;
1219a7e9e7fSjob 
122797cceeeStb 	oder = der;
123797cceeeStb 	if ((cms = d2i_CMS_ContentInfo(NULL, &der, len)) == NULL) {
124c0528901Stb 		warnx("%s: RFC 6488: failed CMS parse", fn);
1259a7e9e7fSjob 		goto out;
1269a7e9e7fSjob 	}
127797cceeeStb 	if (der != oder + len) {
128797cceeeStb 		warnx("%s: %td bytes trailing garbage", fn, oder + len - der);
129797cceeeStb 		goto out;
130797cceeeStb 	}
1319a7e9e7fSjob 
1329a7e9e7fSjob 	/*
1333a50f0a9Sjmc 	 * The CMS is self-signed with a signing certificate.
1349a7e9e7fSjob 	 * Verify that the self-signage is correct.
1359a7e9e7fSjob 	 */
136ef3f6f56Sjob 	if (!CMS_verify(cms, NULL, NULL, bio, NULL,
137cabf3a3bSclaudio 	    CMS_NO_SIGNER_CERT_VERIFY)) {
138c0528901Stb 		warnx("%s: CMS verification error", fn);
1399a7e9e7fSjob 		goto out;
1409a7e9e7fSjob 	}
1419a7e9e7fSjob 
1429025e295Sclaudio 	/* RFC 6488 section 3 verify the CMS */
1439025e295Sclaudio 
14458ffc3d7Stb 	/* Should only return NULL if cms is not of type SignedData. */
14558ffc3d7Stb 	if ((sinfos = CMS_get0_SignerInfos(cms)) == NULL) {
14658ffc3d7Stb 		if ((obj = CMS_get0_type(cms)) == NULL) {
14758ffc3d7Stb 			warnx("%s: RFC 6488: missing content-type", fn);
14858ffc3d7Stb 			goto out;
14958ffc3d7Stb 		}
15058ffc3d7Stb 		OBJ_obj2txt(buf, sizeof(buf), obj, 1);
15158ffc3d7Stb 		warnx("%s: RFC 6488: no signerInfo in CMS object of type %s",
15258ffc3d7Stb 		    fn, buf);
15358ffc3d7Stb 		goto out;
15458ffc3d7Stb 	}
1559025e295Sclaudio 	if (sk_CMS_SignerInfo_num(sinfos) != 1) {
156c0528901Stb 		warnx("%s: RFC 6488: CMS has multiple signerInfos", fn);
1579025e295Sclaudio 		goto out;
1589025e295Sclaudio 	}
1599025e295Sclaudio 	si = sk_CMS_SignerInfo_value(sinfos, 0);
1609025e295Sclaudio 
161de232e1dStb 	if (!CMS_get_version(cms, &version)) {
162de232e1dStb 		warnx("%s: Failed to retrieve SignedData version", fn);
163de232e1dStb 		goto out;
164de232e1dStb 	}
165de232e1dStb 	if (version != 3) {
166de232e1dStb 		warnx("%s: SignedData version %ld != 3", fn, version);
167de232e1dStb 		goto out;
168de232e1dStb 	}
169de232e1dStb 	if (!CMS_SignerInfo_get_version(si, &version)) {
170de232e1dStb 		warnx("%s: Failed to retrieve SignerInfo version", fn);
171de232e1dStb 		goto out;
172de232e1dStb 	}
173de232e1dStb 	if (version != 3) {
174de232e1dStb 		warnx("%s: SignerInfo version %ld != 3", fn, version);
175de232e1dStb 		goto out;
176de232e1dStb 	}
177de232e1dStb 
1789025e295Sclaudio 	nattrs = CMS_signed_get_attr_count(si);
1799025e295Sclaudio 	if (nattrs <= 0) {
180c0528901Stb 		warnx("%s: RFC 6488: error extracting signedAttrs", fn);
1819025e295Sclaudio 		goto out;
1829025e295Sclaudio 	}
1839025e295Sclaudio 	for (i = 0; i < nattrs; i++) {
1849025e295Sclaudio 		X509_ATTRIBUTE *attr;
1859025e295Sclaudio 
1869025e295Sclaudio 		attr = CMS_signed_get_attr(si, i);
1879025e295Sclaudio 		if (attr == NULL || X509_ATTRIBUTE_count(attr) != 1) {
188c0528901Stb 			warnx("%s: RFC 6488: bad signed attribute encoding",
189c0528901Stb 			    fn);
1909025e295Sclaudio 			goto out;
1919025e295Sclaudio 		}
1929025e295Sclaudio 
1939025e295Sclaudio 		obj = X509_ATTRIBUTE_get0_object(attr);
1949025e295Sclaudio 		if (obj == NULL) {
195c0528901Stb 			warnx("%s: RFC 6488: bad signed attribute", fn);
1969025e295Sclaudio 			goto out;
1979025e295Sclaudio 		}
1989025e295Sclaudio 		if (OBJ_cmp(obj, cnt_type_oid) == 0) {
1999025e295Sclaudio 			if (has_ct++ != 0) {
200c0528901Stb 				warnx("%s: RFC 6488: duplicate "
2019025e295Sclaudio 				    "signed attribute", fn);
2029025e295Sclaudio 				goto out;
2039025e295Sclaudio 			}
2049025e295Sclaudio 		} else if (OBJ_cmp(obj, msg_dgst_oid) == 0) {
2059025e295Sclaudio 			if (has_md++ != 0) {
206c0528901Stb 				warnx("%s: RFC 6488: duplicate "
2079025e295Sclaudio 				    "signed attribute", fn);
2089025e295Sclaudio 				goto out;
2099025e295Sclaudio 			}
2109025e295Sclaudio 		} else if (OBJ_cmp(obj, sign_time_oid) == 0) {
2119025e295Sclaudio 			if (has_st++ != 0) {
212c0528901Stb 				warnx("%s: RFC 6488: duplicate "
2139025e295Sclaudio 				    "signed attribute", fn);
2149025e295Sclaudio 				goto out;
2159025e295Sclaudio 			}
2161bb1e509Sjob 			if (!cms_get_signtime(fn, attr, signtime))
2171bb1e509Sjob 				goto out;
2189025e295Sclaudio 		} else {
2199025e295Sclaudio 			OBJ_obj2txt(buf, sizeof(buf), obj, 1);
220c0528901Stb 			warnx("%s: RFC 6488: "
2219025e295Sclaudio 			    "CMS has unexpected signed attribute %s",
2229025e295Sclaudio 			    fn, buf);
2239025e295Sclaudio 			goto out;
2249025e295Sclaudio 		}
2259025e295Sclaudio 	}
2261bb1e509Sjob 
2279025e295Sclaudio 	if (!has_ct || !has_md) {
228c7a965b3Stb 		/* RFC 9589, section 4 */
229c0528901Stb 		warnx("%s: RFC 6488: CMS missing required "
2309025e295Sclaudio 		    "signed attribute", fn);
2319025e295Sclaudio 		goto out;
2329025e295Sclaudio 	}
2331bb1e509Sjob 
234968e2494Sjob 	if (!has_st) {
235c7a965b3Stb 		/* RFC 9589, section 4 */
2361bce4d34Sjob 		warnx("%s: missing CMS signing-time attribute", fn);
237968e2494Sjob 		goto out;
238968e2494Sjob 	}
2391bce4d34Sjob 
240974c6fafStb 	if (CMS_unsigned_get_attr_count(si) != -1) {
241c0528901Stb 		warnx("%s: RFC 6488: CMS has unsignedAttrs", fn);
2429025e295Sclaudio 		goto out;
2439025e295Sclaudio 	}
2449025e295Sclaudio 
245ae36eebeSjob 	/* Check digest and signature algorithms (RFC 7935) */
246ae36eebeSjob 	CMS_SignerInfo_get0_algs(si, &pkey, NULL, &pdig, &psig);
247ae36eebeSjob 	if (!valid_ca_pkey(fn, pkey))
248ae36eebeSjob 		goto out;
249ae36eebeSjob 
2509025e295Sclaudio 	X509_ALGOR_get0(&obj, NULL, NULL, pdig);
2519025e295Sclaudio 	nid = OBJ_obj2nid(obj);
2529025e295Sclaudio 	if (nid != NID_sha256) {
2539025e295Sclaudio 		warnx("%s: RFC 6488: wrong digest %s, want %s", fn,
25478de3577Stb 		    nid2str(nid), LN_sha256);
2559025e295Sclaudio 		goto out;
2569025e295Sclaudio 	}
2579025e295Sclaudio 	X509_ALGOR_get0(&obj, NULL, NULL, psig);
2589025e295Sclaudio 	nid = OBJ_obj2nid(obj);
259e5fe0df7Sjob 	/* RFC7935 last paragraph of section 2 specifies the allowed psig */
26081a06611Sclaudio 	if (experimental && nid == NID_ecdsa_with_SHA256) {
261ec1cc732Sjob 		if (verbose)
2628fcc9cc2Sjob 			warnx("%s: P-256 support is experimental", fn);
263ec1cc732Sjob 	} else if (nid != NID_rsaEncryption &&
264ec1cc732Sjob 	    nid != NID_sha256WithRSAEncryption) {
2659025e295Sclaudio 		warnx("%s: RFC 6488: wrong signature algorithm %s, want %s",
26678de3577Stb 		    fn, nid2str(nid), LN_rsaEncryption);
2679025e295Sclaudio 		goto out;
2689025e295Sclaudio 	}
2699025e295Sclaudio 
2709a7e9e7fSjob 	/* RFC 6488 section 2.1.3.1: check the object's eContentType. */
2719a7e9e7fSjob 
2729a7e9e7fSjob 	obj = CMS_get0_eContentType(cms);
273d2e465bbSclaudio 	if (obj == NULL) {
2749a7e9e7fSjob 		warnx("%s: RFC 6488 section 2.1.3.1: eContentType: "
275d2e465bbSclaudio 		    "OID object is NULL", fn);
276d2e465bbSclaudio 		goto out;
277d2e465bbSclaudio 	}
278d2e465bbSclaudio 	if (OBJ_cmp(obj, oid) != 0) {
279d2e465bbSclaudio 		OBJ_obj2txt(buf, sizeof(buf), obj, 1);
280d2e465bbSclaudio 		OBJ_obj2txt(obuf, sizeof(obuf), oid, 1);
281d2e465bbSclaudio 		warnx("%s: RFC 6488 section 2.1.3.1: eContentType: "
282d2e465bbSclaudio 		    "unknown OID: %s, want %s", fn, buf, obuf);
2839a7e9e7fSjob 		goto out;
2849a7e9e7fSjob 	}
2859a7e9e7fSjob 
2869025e295Sclaudio 	/* Compare content-type with eContentType */
2879025e295Sclaudio 	octype = CMS_signed_get0_data_by_OBJ(si, cnt_type_oid,
2889025e295Sclaudio 	    -3, V_ASN1_OBJECT);
289233f224dStb 	/*
290233f224dStb 	 * Since lastpos == -3, octype can be NULL for 4 reasons:
291233f224dStb 	 * 1. requested attribute OID is missing
292233f224dStb 	 * 2. signedAttrs contains multiple attributes with requested OID
293233f224dStb 	 * 3. attribute with requested OID has multiple values (malformed)
294c026bd36Stb 	 * 4. X509_ATTRIBUTE_get0_data() returned NULL. This is also malformed,
295233f224dStb 	 *    but libcrypto will create, sign, and verify such objects.
296233f224dStb 	 * Reasons 1 and 2 are excluded because has_ct == 1. We don't know which
297233f224dStb 	 * one of 3 or 4 we hit. Doesn't matter, drop the garbage on the floor.
298233f224dStb 	 */
299233f224dStb 	if (octype == NULL) {
300233f224dStb 		warnx("%s: RFC 6488, section 2.1.6.4.1: malformed value "
301233f224dStb 		    "for content-type attribute", fn);
302233f224dStb 		goto out;
303233f224dStb 	}
3049025e295Sclaudio 	if (OBJ_cmp(obj, octype) != 0) {
3059025e295Sclaudio 		OBJ_obj2txt(buf, sizeof(buf), obj, 1);
30677251745Sjob 		OBJ_obj2txt(obuf, sizeof(obuf), octype, 1);
3079025e295Sclaudio 		warnx("%s: RFC 6488: eContentType does not match Content-Type "
3089025e295Sclaudio 		    "OID: %s, want %s", fn, buf, obuf);
3099025e295Sclaudio 		goto out;
3109025e295Sclaudio 	}
3119025e295Sclaudio 
3129025e295Sclaudio 	/*
3139025e295Sclaudio 	 * Check that there are no CRLS in this CMS message.
314475ff837Sjob 	 * XXX - can only error check for OpenSSL >= 3.4.
3159025e295Sclaudio 	 */
3169025e295Sclaudio 	crls = CMS_get1_crls(cms);
317475ff837Sjob 	if (crls != NULL && sk_X509_CRL_num(crls) != 0) {
318c0528901Stb 		warnx("%s: RFC 6488: CMS has CRLs", fn);
3199025e295Sclaudio 		goto out;
3209025e295Sclaudio 	}
3219025e295Sclaudio 
3229a7e9e7fSjob 	/*
3239a7e9e7fSjob 	 * The self-signing certificate is further signed by the input
3249a7e9e7fSjob 	 * signing authority according to RFC 6488, 2.1.4.
3259a7e9e7fSjob 	 * We extract that certificate now for later verification.
3269a7e9e7fSjob 	 */
3279a7e9e7fSjob 
3289a7e9e7fSjob 	certs = CMS_get0_signers(cms);
3299a7e9e7fSjob 	if (certs == NULL || sk_X509_num(certs) != 1) {
33080272c49Sderaadt 		warnx("%s: RFC 6488 section 2.1.4: eContent: "
33180272c49Sderaadt 		    "want 1 signer, have %d", fn, sk_X509_num(certs));
3329a7e9e7fSjob 		goto out;
3339a7e9e7fSjob 	}
33491b15523Stb 	*xp = sk_X509_value(certs, 0);
33591b15523Stb 	if (!X509_up_ref(*xp)) {
33691b15523Stb 		*xp = NULL;
33791b15523Stb 		goto out;
33891b15523Stb 	}
3399a7e9e7fSjob 
340e891962dStb 	if (!x509_cache_extensions(*xp, fn))
341b2d6bcdcStb 		goto out;
342b2d6bcdcStb 
343db13aa11Sjob 	if (!x509_get_notafter(*xp, fn, &notafter))
344db13aa11Sjob 		goto out;
345b3614c08Sjob 	if (*signtime > notafter)
346db13aa11Sjob 		warnx("%s: dating issue: CMS signing-time after X.509 notAfter",
347db13aa11Sjob 		    fn);
348db13aa11Sjob 
349570c9579Sclaudio 	if (CMS_SignerInfo_get0_signer_id(si, &kid, NULL, NULL) != 1 ||
350570c9579Sclaudio 	    kid == NULL) {
351570c9579Sclaudio 		warnx("%s: RFC 6488: could not extract SKI from SID", fn);
352570c9579Sclaudio 		goto out;
353570c9579Sclaudio 	}
354570c9579Sclaudio 	if (CMS_SignerInfo_cert_cmp(si, *xp) != 0) {
355570c9579Sclaudio 		warnx("%s: RFC 6488: wrong cert referenced by SignerInfo", fn);
356570c9579Sclaudio 		goto out;
357570c9579Sclaudio 	}
358570c9579Sclaudio 
359f8cba460Stb 	if (!cms_extract_econtent(fn, cms, res, rsz))
360ef3f6f56Sjob 		goto out;
3619a7e9e7fSjob 
3629a7e9e7fSjob 	rc = 1;
3639a7e9e7fSjob  out:
3649a7e9e7fSjob 	if (rc == 0) {
3659a7e9e7fSjob 		X509_free(*xp);
3669a7e9e7fSjob 		*xp = NULL;
3679a7e9e7fSjob 	}
368475ff837Sjob 	sk_X509_CRL_pop_free(crls, X509_CRL_free);
369ef3f6f56Sjob 	sk_X509_free(certs);
370ef3f6f56Sjob 	CMS_ContentInfo_free(cms);
371ef3f6f56Sjob 	return rc;
372ef3f6f56Sjob }
373ef3f6f56Sjob 
374ef3f6f56Sjob /*
375ef3f6f56Sjob  * Parse and validate a self-signed CMS message.
376ef3f6f56Sjob  * Conforms to RFC 6488.
377ef3f6f56Sjob  * The eContentType of the message must be an oid object.
378ef3f6f56Sjob  * Return the eContent as a string and set "rsz" to be its length.
379ef3f6f56Sjob  */
380ef3f6f56Sjob unsigned char *
381ef3f6f56Sjob cms_parse_validate(X509 **xp, const char *fn, const unsigned char *der,
3821bb1e509Sjob     size_t derlen, const ASN1_OBJECT *oid, size_t *rsz, time_t *st)
383ef3f6f56Sjob {
384ef3f6f56Sjob 	unsigned char *res = NULL;
385ef3f6f56Sjob 
386ef3f6f56Sjob 	if (!cms_parse_validate_internal(xp, fn, der, derlen, oid, NULL, &res,
3871bb1e509Sjob 	    rsz, st))
388ef3f6f56Sjob 		return NULL;
3899a7e9e7fSjob 
3909a7e9e7fSjob 	return res;
3919a7e9e7fSjob }
392ef3f6f56Sjob 
393ef3f6f56Sjob /*
394ef3f6f56Sjob  * Parse and validate a detached CMS signature.
395ef3f6f56Sjob  * bio must contain the original message, der must contain the CMS.
396ef3f6f56Sjob  * Return the 1 on success, 0 on failure.
397ef3f6f56Sjob  */
398ef3f6f56Sjob int
399ef3f6f56Sjob cms_parse_validate_detached(X509 **xp, const char *fn, const unsigned char *der,
4001bb1e509Sjob     size_t derlen, const ASN1_OBJECT *oid, BIO *bio, time_t *st)
401ef3f6f56Sjob {
402ef3f6f56Sjob 	return cms_parse_validate_internal(xp, fn, der, derlen, oid, bio, NULL,
4031bb1e509Sjob 	    NULL, st);
404ef3f6f56Sjob }
405