By design of d2i, it's the caller's responsibility to check a DER object
has been fully consumed. We read files from the disk, check hashes,
parse and validate the DER we encounter, but we do not make sure that
nothing follows the DER blob we parsed.

As Job noticed, it is possible to append data to a CRL and still have a
manifest display "Validation: OK" in file mode. This is partly possible
due to the fact that filemode has a rather lax notion of validity (since
it is an inspection tool), but also due to these missing checks.

The diff below checks for !=. Barring bugs in ASN1_item_d2i() (unheard
of!), only the < case should be possible, but it seems better to allow
for > as well. I guess we could assert <=.

Index: cert.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/cert.c,v
retrieving revision 1.101
diff -u -p -r1.101 cert.c
--- cert.c      30 Nov 2022 09:12:34 -0000      1.101
+++ cert.c      21 Feb 2023 01:48:00 -0000
@@ -641,13 +641,14 @@ cert_parse_ee_cert(const char *fn, X509 
 struct cert *
 cert_parse_pre(const char *fn, const unsigned char *der, size_t len)
 {
-       int              extsz;
-       int              sia_present = 0;
-       size_t           i;
-       X509            *x = NULL;
-       X509_EXTENSION  *ext = NULL;
-       ASN1_OBJECT     *obj;
-       struct parse     p;
+       const unsigned char     *oder;
+       int                      extsz;
+       int                      sia_present = 0;
+       size_t                   i;
+       X509                    *x = NULL;
+       X509_EXTENSION          *ext = NULL;
+       ASN1_OBJECT             *obj;
+       struct parse             p;
 
        /* just fail for empty buffers, the warning was printed elsewhere */
        if (der == NULL)
@@ -658,8 +659,13 @@ cert_parse_pre(const char *fn, const uns
        if ((p.res = calloc(1, sizeof(struct cert))) == NULL)
                err(1, NULL);
 
+       oder = der;
        if ((x = d2i_X509(NULL, &der, len)) == NULL) {
                cryptowarnx("%s: d2i_X509", p.fn);
+               goto out;
+       }
+       if (der != oder + len) {
+               warnx("%s: %td bytes trailing garbage", fn, oder + len - der);
                goto out;
        }
 
Index: cms.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/cms.c,v
retrieving revision 1.26
diff -u -p -r1.26 cms.c
--- cms.c       28 Dec 2022 21:30:18 -0000      1.26
+++ cms.c       21 Feb 2023 01:45:37 -0000
@@ -64,9 +64,10 @@ cms_extract_econtent(const char *fn, CMS
 
 static int
 cms_parse_validate_internal(X509 **xp, const char *fn, const unsigned char 
*der,
-    size_t derlen, const ASN1_OBJECT *oid, BIO *bio, unsigned char **res,
+    size_t len, const ASN1_OBJECT *oid, BIO *bio, unsigned char **res,
     size_t *rsz)
 {
+       const unsigned char             *oder;
        char                             buf[128], obuf[128];
        const ASN1_OBJECT               *obj, *octype;
        ASN1_OCTET_STRING               *kid = NULL;
@@ -89,8 +90,13 @@ cms_parse_validate_internal(X509 **xp, c
        if (der == NULL)
                return 0;
 
-       if ((cms = d2i_CMS_ContentInfo(NULL, &der, derlen)) == NULL) {
+       oder = der;
+       if ((cms = d2i_CMS_ContentInfo(NULL, &der, len)) == NULL) {
                cryptowarnx("%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;
        }
 
Index: crl.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/crl.c,v
retrieving revision 1.21
diff -u -p -r1.21 crl.c
--- crl.c       30 Nov 2022 09:03:44 -0000      1.21
+++ crl.c       21 Feb 2023 01:47:31 -0000
@@ -25,9 +25,10 @@
 struct crl *
 crl_parse(const char *fn, const unsigned char *der, size_t len)
 {
-       struct crl      *crl;
-       const ASN1_TIME *at;
-       int              rc = 0;
+       const unsigned char     *oder;
+       struct crl              *crl;
+       const ASN1_TIME         *at;
+       int                      rc = 0;
 
        /* just fail for empty buffers, the warning was printed elsewhere */
        if (der == NULL)
@@ -36,8 +37,13 @@ crl_parse(const char *fn, const unsigned
        if ((crl = calloc(1, sizeof(*crl))) == NULL)
                err(1, NULL);
 
+       oder = der;
        if ((crl->x509_crl = d2i_X509_CRL(NULL, &der, len)) == NULL) {
                cryptowarnx("%s: d2i_X509_CRL", fn);
+               goto out;
+       }
+       if (der != oder + len) {
+               warnx("%s: %td bytes trailing garbage", fn, oder + len - der);
                goto out;
        }
 

Reply via email to