Download raw body.
rpki-client: hoist up cert/crl checks in cms
On Thu, Sep 03, 2026 at 10:43:02PM +0200, Theo Buehler wrote:
> This moves the retrieval of the EE cert and the check of absence of CRLs
> to the earliest possible moment. The signtime check must come after we
> extracted the signed attributes so it's a bit lonely right now. We can
> fix this later.
One reason I wanted to do this was to reduce the noise in a follow up.
However, below is a slightly larger diff that should be easy enough to
review. It effectively still hoists the cert/crl checks but looks quite
different.
There is a huge messy part in the CMS validation code that deals with
things needing the SignerInfo (the OpenSSL version of it, that is).
All this stuff does not need cleanup at exit and is very long, boring
and meandering code which is concerned with requirements from RFC 6488
which are largely independent of the econtent validation and parsing.
So pull all this into a helper and pass in the few things we need.
The only info we want to retain is that the object is well-formed and
its signtime. Since we need to pass in the cert, we call the helper
after extraction and before validation of the signtime against the cert.
The main point is that cms_parse_validate() finally becomes somewhat
manageable in size.
Index: cms.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/cms.c,v
diff -u -p -r1.64 cms.c
--- cms.c 3 Sep 2026 19:10:35 -0000 1.64
+++ cms.c 4 Sep 2026 11:13:11 -0000
@@ -175,102 +175,54 @@ cms_SignerInfo_check_attributes(const ch
}
static int
-cms_parse_validate(struct cert **out_cert, const char *fn, int talid,
- const unsigned char *der, size_t len, const ASN1_OBJECT *oid,
- unsigned char **res, size_t *rsz, time_t *signtime)
+cms_check_SignerInfo(const char *fn, CMS_ContentInfo *cms,
+ const ASN1_OBJECT *oid, struct cert *cert, time_t *signtime)
{
- struct cert *cert = NULL;
- const unsigned char *oder;
char buf[128], obuf[128];
const ASN1_OBJECT *obj, *octype;
ASN1_OCTET_STRING *kid = NULL;
- CMS_ContentInfo *cms = NULL;
long version;
- STACK_OF(X509) *certs = NULL;
- STACK_OF(X509_CRL) *crls = NULL;
STACK_OF(CMS_SignerInfo) *sinfos;
CMS_SignerInfo *si;
X509_ALGOR *pdig, *psig;
int nid;
- int rc = 0;
-
- assert(*out_cert == NULL);
-
- *signtime = 0;
-
- /* just fail for empty buffers, the warning was printed elsewhere */
- if (der == NULL)
- return 0;
-
- if (len < 2) {
- warnx("%s: RFC 6488: CMS encoding too short", fn);
- goto out;
- }
- if (der[0] == ASN1_TAG_SEQUENCE && der[1] == ASN1_LENGTH_INDEFINITE) {
- warnx("%s: RFC 6488: indefinite length encoding disallowed "
- "in DER", fn);
- if (!filemode)
- goto out;
- }
-
- oder = der;
- if ((cms = d2i_CMS_ContentInfo(NULL, &der, len)) == NULL) {
- warnx("%s: RFC 6488: failed CMS parse", fn);
- goto out;
- }
- if (der != oder + len) {
- warnx("%s: %td bytes trailing garbage", fn, oder + len - der);
- goto out;
- }
-
- /*
- * The CMS is self-signed with a signing certificate.
- * Verify that the self-signage is correct.
- */
- if (!CMS_verify(cms, NULL, NULL, NULL, NULL,
- CMS_NO_SIGNER_CERT_VERIFY)) {
- warnx("%s: CMS verification error", fn);
- goto out;
- }
-
- /* RFC 6488 section 3 verify the CMS */
/* Should only return NULL if cms is not of type SignedData. */
if ((sinfos = CMS_get0_SignerInfos(cms)) == NULL) {
if ((obj = CMS_get0_type(cms)) == NULL) {
warnx("%s: RFC 6488: missing content-type", fn);
- goto out;
+ return 0;
}
OBJ_obj2txt(buf, sizeof(buf), obj, 1);
warnx("%s: RFC 6488: no signerInfo in CMS object of type %s",
fn, buf);
- goto out;
+ return 0;
}
if (sk_CMS_SignerInfo_num(sinfos) != 1) {
warnx("%s: RFC 6488: CMS has multiple signerInfos", fn);
- goto out;
+ return 0;
}
si = sk_CMS_SignerInfo_value(sinfos, 0);
if (!CMS_get_version(cms, &version)) {
warnx("%s: Failed to retrieve SignedData version", fn);
- goto out;
+ return 0;
}
if (version != 3) {
warnx("%s: SignedData version %ld != 3", fn, version);
- goto out;
+ return 0;
}
if (!CMS_SignerInfo_get_version(si, &version)) {
warnx("%s: Failed to retrieve SignerInfo version", fn);
- goto out;
+ return 0;
}
if (version != 3) {
warnx("%s: SignerInfo version %ld != 3", fn, version);
- goto out;
+ return 0;
}
if (!cms_SignerInfo_check_attributes(fn, si, signtime))
- goto out;
+ return 0;
/* Check digest and signature algorithms (RFC 7935) */
CMS_SignerInfo_get0_algs(si, NULL, NULL, &pdig, &psig);
@@ -280,7 +232,7 @@ cms_parse_validate(struct cert **out_cer
if (nid != NID_sha256) {
warnx("%s: RFC 6488: wrong digest %s, want %s", fn,
nid2str(nid), LN_sha256);
- goto out;
+ return 0;
}
X509_ALGOR_get0(&obj, NULL, NULL, psig);
nid = OBJ_obj2nid(obj);
@@ -292,7 +244,7 @@ cms_parse_validate(struct cert **out_cer
nid != NID_sha256WithRSAEncryption) {
warnx("%s: RFC 6488: wrong signature algorithm %s, want %s",
fn, nid2str(nid), LN_rsaEncryption);
- goto out;
+ return 0;
}
/* RFC 6488 section 2.1.3.1: check the object's eContentType. */
@@ -301,14 +253,14 @@ cms_parse_validate(struct cert **out_cer
if (obj == NULL) {
warnx("%s: RFC 6488 section 2.1.3.1: eContentType: "
"OID object is NULL", fn);
- goto out;
+ return 0;
}
if (OBJ_cmp(obj, oid) != 0) {
OBJ_obj2txt(buf, sizeof(buf), obj, 1);
OBJ_obj2txt(obuf, sizeof(obuf), oid, 1);
warnx("%s: RFC 6488 section 2.1.3.1: eContentType: "
"unknown OID: %s, want %s", fn, buf, obuf);
- goto out;
+ return 0;
}
/* Compare content-type with eContentType */
@@ -327,13 +279,77 @@ cms_parse_validate(struct cert **out_cer
if (octype == NULL) {
warnx("%s: RFC 6488, section 2.1.6.4.1: malformed value "
"for content-type attribute", fn);
- goto out;
+ return 0;
}
if (OBJ_cmp(obj, octype) != 0) {
OBJ_obj2txt(buf, sizeof(buf), obj, 1);
OBJ_obj2txt(obuf, sizeof(obuf), octype, 1);
warnx("%s: RFC 6488: eContentType does not match Content-Type "
"OID: %s, want %s", fn, buf, obuf);
+ return 0;
+ }
+
+ if (CMS_SignerInfo_get0_signer_id(si, &kid, NULL, NULL) != 1 ||
+ kid == NULL) {
+ warnx("%s: RFC 6488: could not extract SKI from SID", fn);
+ return 0;
+ }
+ if (CMS_SignerInfo_cert_cmp(si, cert->x509) != 0) {
+ warnx("%s: RFC 6488: wrong cert referenced by SignerInfo", fn);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+cms_parse_validate(struct cert **out_cert, const char *fn, int talid,
+ const unsigned char *der, size_t len, const ASN1_OBJECT *oid,
+ unsigned char **res, size_t *rsz, time_t *signtime)
+{
+ struct cert *cert = NULL;
+ const unsigned char *oder;
+ CMS_ContentInfo *cms = NULL;
+ STACK_OF(X509) *certs = NULL;
+ STACK_OF(X509_CRL) *crls = NULL;
+ int rc = 0;
+
+ assert(*out_cert == NULL);
+
+ *signtime = 0;
+
+ /* just fail for empty buffers, the warning was printed elsewhere */
+ if (der == NULL)
+ return 0;
+
+ if (len < 2) {
+ warnx("%s: RFC 6488: CMS encoding too short", fn);
+ goto out;
+ }
+ if (der[0] == ASN1_TAG_SEQUENCE && der[1] == ASN1_LENGTH_INDEFINITE) {
+ warnx("%s: RFC 6488: indefinite length encoding disallowed "
+ "in DER", fn);
+ if (!filemode)
+ goto out;
+ }
+
+ oder = der;
+ if ((cms = d2i_CMS_ContentInfo(NULL, &der, len)) == NULL) {
+ warnx("%s: RFC 6488: failed CMS parse", fn);
+ goto out;
+ }
+ if (der != oder + len) {
+ warnx("%s: %td bytes trailing garbage", fn, oder + len - der);
+ goto out;
+ }
+
+ /*
+ * The CMS is self-signed with a signing certificate.
+ * Verify that the self-signage is correct.
+ */
+ if (!CMS_verify(cms, NULL, NULL, NULL, NULL,
+ CMS_NO_SIGNER_CERT_VERIFY)) {
+ warnx("%s: CMS verification error", fn);
goto out;
}
@@ -364,19 +380,13 @@ cms_parse_validate(struct cert **out_cer
if (cert == NULL)
goto out;
+ /* RFC 6488 section 3 verify the CMS */
+ if (!cms_check_SignerInfo(fn, cms, oid, cert, signtime))
+ goto out;
+
if (*signtime > cert->notafter)
warnx("%s: dating issue: CMS signing-time after X.509 notAfter",
fn);
-
- if (CMS_SignerInfo_get0_signer_id(si, &kid, NULL, NULL) != 1 ||
- kid == NULL) {
- warnx("%s: RFC 6488: could not extract SKI from SID", fn);
- goto out;
- }
- if (CMS_SignerInfo_cert_cmp(si, cert->x509) != 0) {
- warnx("%s: RFC 6488: wrong cert referenced by SignerInfo", fn);
- goto out;
- }
if (!cms_extract_econtent(fn, cms, res, rsz))
goto out;
rpki-client: hoist up cert/crl checks in cms