Rewrite mft.c and roa.c with ASN.1 templates
authortb <tb@openbsd.org>
Thu, 19 May 2022 06:37:51 +0000 (06:37 +0000)
committertb <tb@openbsd.org>
Thu, 19 May 2022 06:37:51 +0000 (06:37 +0000)
The ASN.1 templates are a rather direct translation of the ASN.1 in the
relevant RFCs and they allow deserializing the Manifest and ROA eContent
in a single step instead of numerous opaque d2i_ASN1_SEQUENCE_ANY() calls.
Once the eContent is deserialized, we can walk the structs, validate it
as before and populate the internal data structures.

Positive feedback job
ok claudio

usr.sbin/rpki-client/mft.c
usr.sbin/rpki-client/roa.c

index eb8e469..cf7ab80 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: mft.c,v 1.65 2022/05/15 15:00:53 deraadt Exp $ */
+/*     $OpenBSD: mft.c,v 1.66 2022/05/19 06:37:51 tb Exp $ */
 /*
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
  *
 
 #include <openssl/bn.h>
 #include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/safestack.h>
 #include <openssl/sha.h>
+#include <openssl/stack.h>
 #include <openssl/x509.h>
 
 #include "extern.h"
@@ -43,6 +46,48 @@ struct       parse {
 
 extern ASN1_OBJECT     *mft_oid;
 
+/*
+ * Types and templates for the Manifest eContent, RFC 6486, section 4.2.
+ */
+
+typedef struct {
+       ASN1_IA5STRING  *file;
+       ASN1_BIT_STRING *hash;
+} FileAndHash;
+
+DECLARE_STACK_OF(FileAndHash);
+
+#if defined(LIBRESSL_VERSION_NUMBER)
+#define sk_FileAndHash_num(sk)         SKM_sk_num(FileAndHash, (sk))
+#define sk_FileAndHash_value(sk, i)    SKM_sk_value(FileAndHash, (sk), (i))
+#endif
+
+typedef struct {
+       ASN1_INTEGER            *version;
+       ASN1_INTEGER            *manifestNumber;
+       ASN1_GENERALIZEDTIME    *thisUpdate;
+       ASN1_GENERALIZEDTIME    *nextUpdate;
+       ASN1_OBJECT             *fileHashAlg;
+       STACK_OF(FileAndHash)   *fileList;
+} Manifest;
+
+ASN1_SEQUENCE(FileAndHash) = {
+       ASN1_SIMPLE(FileAndHash, file, ASN1_IA5STRING),
+       ASN1_SIMPLE(FileAndHash, hash, ASN1_BIT_STRING),
+} ASN1_SEQUENCE_END(FileAndHash);
+
+ASN1_SEQUENCE(Manifest) = {
+       ASN1_IMP_OPT(Manifest, version, ASN1_INTEGER, 0),
+       ASN1_SIMPLE(Manifest, manifestNumber, ASN1_INTEGER),
+       ASN1_SIMPLE(Manifest, thisUpdate, ASN1_GENERALIZEDTIME),
+       ASN1_SIMPLE(Manifest, nextUpdate, ASN1_GENERALIZEDTIME),
+       ASN1_SIMPLE(Manifest, fileHashAlg, ASN1_OBJECT),
+       ASN1_SEQUENCE_OF(Manifest, fileList, FileAndHash),
+} ASN1_SEQUENCE_END(Manifest);
+
+DECLARE_ASN1_FUNCTIONS(Manifest);
+IMPLEMENT_ASN1_FUNCTIONS(Manifest);
+
 /*
  * Convert an ASN1_GENERALIZEDTIME to a struct tm.
  * Returns 1 on success, 0 on failure.
@@ -173,70 +218,36 @@ rtype_from_mftfile(const char *fn)
  * Return zero on failure, non-zero on success.
  */
 static int
-mft_parse_filehash(struct parse *p, const ASN1_OCTET_STRING *os)
+mft_parse_filehash(struct parse *p, const FileAndHash *fh)
 {
-       ASN1_SEQUENCE_ANY       *seq;
-       const ASN1_TYPE         *file, *hash;
        char                    *fn = NULL;
-       const unsigned char     *d = os->data;
-       size_t                   dsz = os->length;
        int                      rc = 0;
        struct mftfile          *fent;
        enum rtype               type;
 
-       if ((seq = d2i_ASN1_SEQUENCE_ANY(NULL, &d, dsz)) == NULL) {
-               cryptowarnx("%s: RFC 6486 section 4.2.1: FileAndHash: "
-                   "failed ASN.1 sequence parse", p->fn);
-               goto out;
-       }
-       if (sk_ASN1_TYPE_num(seq) != 2) {
-               warnx("%s: RFC 6486 section 4.2.1: FileAndHash: "
-                   "want 2 elements, have %d", p->fn,
-                   sk_ASN1_TYPE_num(seq));
-               goto out;
-       }
-
        /* First is the filename itself. */
 
-       file = sk_ASN1_TYPE_value(seq, 0);
-       if (file->type != V_ASN1_IA5STRING) {
-               warnx("%s: RFC 6486 section 4.2.1: FileAndHash: "
-                   "want ASN.1 IA5 string, have %s (NID %d)",
-                   p->fn, ASN1_tag2str(file->type), file->type);
-               goto out;
-       }
-       if (!valid_mft_filename(file->value.ia5string->data,
-           file->value.ia5string->length)) {
+       if (!valid_mft_filename(fh->file->data, fh->file->length)) {
                warnx("%s: RFC 6486 section 4.2.2: bad filename", p->fn);
                goto out;
        }
-       fn = strndup((const char *)file->value.ia5string->data,
-           file->value.ia5string->length);
+       fn = strndup(fh->file->data, fh->file->length);
        if (fn == NULL)
                err(1, NULL);
 
        /* Now hash value. */
 
-       hash = sk_ASN1_TYPE_value(seq, 1);
-       if (hash->type != V_ASN1_BIT_STRING) {
-               warnx("%s: RFC 6486 section 4.2.1: FileAndHash: "
-                   "want ASN.1 bit string, have %s (NID %d)",
-                   p->fn, ASN1_tag2str(hash->type), hash->type);
-               goto out;
-       }
-
-       if (hash->value.bit_string->length != SHA256_DIGEST_LENGTH) {
+       if (fh->hash->length != SHA256_DIGEST_LENGTH) {
                warnx("%s: RFC 6486 section 4.2.1: hash: "
                    "invalid SHA256 length, have %d",
-                   p->fn, hash->value.bit_string->length);
+                   p->fn, fh->hash->length);
                goto out;
        }
 
        type = rtype_from_mftfile(fn);
        /* remember the filehash for the CRL in struct mft */
        if (type == RTYPE_CRL && strcmp(fn, p->res->crl) == 0) {
-               memcpy(p->res->crlhash, hash->value.bit_string->data,
-                   SHA256_DIGEST_LENGTH);
+               memcpy(p->res->crlhash, fh->hash->data, SHA256_DIGEST_LENGTH);
                p->found_crl = 1;
        }
 
@@ -245,64 +256,11 @@ mft_parse_filehash(struct parse *p, const ASN1_OCTET_STRING *os)
        fent->type = type;
        fent->file = fn;
        fn = NULL;
-       memcpy(fent->hash, hash->value.bit_string->data, SHA256_DIGEST_LENGTH);
-
-       rc = 1;
-out:
-       free(fn);
-       sk_ASN1_TYPE_pop_free(seq, ASN1_TYPE_free);
-       return rc;
-}
-
-/*
- * Parse the "FileAndHash" sequence, RFC 6486, sec. 4.2.
- * Return zero on failure, non-zero on success.
- */
-static int
-mft_parse_flist(struct parse *p, const ASN1_OCTET_STRING *os)
-{
-       ASN1_SEQUENCE_ANY       *seq;
-       const ASN1_TYPE         *t;
-       const unsigned char     *d = os->data;
-       size_t                   dsz = os->length;
-       int                      i, rc = 0;
-
-       if ((seq = d2i_ASN1_SEQUENCE_ANY(NULL, &d, dsz)) == NULL) {
-               cryptowarnx("%s: RFC 6486 section 4.2: fileList: "
-                   "failed ASN.1 sequence parse", p->fn);
-               goto out;
-       }
-
-       if (sk_ASN1_TYPE_num(seq) > MAX_MANIFEST_ENTRIES) {
-               warnx("%s: %d exceeds manifest entry limit (%d)", p->fn,
-                   sk_ASN1_TYPE_num(seq), MAX_MANIFEST_ENTRIES);
-               goto out;
-       }
-
-       p->res->files = calloc(sk_ASN1_TYPE_num(seq), sizeof(struct mftfile));
-       if (p->res->files == NULL)
-               err(1, NULL);
-
-       for (i = 0; i < sk_ASN1_TYPE_num(seq); i++) {
-               t = sk_ASN1_TYPE_value(seq, i);
-               if (t->type != V_ASN1_SEQUENCE) {
-                       warnx("%s: RFC 6486 section 4.2: fileList: "
-                           "want ASN.1 sequence, have %s (NID %d)",
-                           p->fn, ASN1_tag2str(t->type), t->type);
-                       goto out;
-               }
-               if (!mft_parse_filehash(p, t->value.octet_string))
-                       goto out;
-       }
-
-       if (!p->found_crl) {
-               warnx("%s: CRL not part of MFT fileList", p->fn);
-               goto out;
-       }
+       memcpy(fent->hash, fh->hash->data, SHA256_DIGEST_LENGTH);
 
        rc = 1;
  out:
-       sk_ASN1_TYPE_pop_free(seq, ASN1_TYPE_free);
+       free(fn);
        return rc;
 }
 
@@ -313,35 +271,24 @@ mft_parse_flist(struct parse *p, const ASN1_OCTET_STRING *os)
 static int
 mft_parse_econtent(const unsigned char *d, size_t dsz, struct parse *p)
 {
-       ASN1_SEQUENCE_ANY       *seq;
-       const ASN1_TYPE         *t;
-       const ASN1_GENERALIZEDTIME *from, *until;
+       Manifest                *mft;
+       FileAndHash             *fh;
        long                     mft_version;
-       int                      i = 0, rc = 0;
+       int                      i, rc = 0;
 
-       if ((seq = d2i_ASN1_SEQUENCE_ANY(NULL, &d, dsz)) == NULL) {
+       if ((mft = d2i_Manifest(NULL, &d, dsz)) == NULL) {
                cryptowarnx("%s: RFC 6486 section 4.2: Manifest: "
                    "failed ASN.1 sequence parse", p->fn);
                goto out;
        }
 
-       /* Test if the optional profile version field is present. */
-       if (sk_ASN1_TYPE_num(seq) != 5 &&
-           sk_ASN1_TYPE_num(seq) != 6) {
-               warnx("%s: RFC 6486 section 4.2: Manifest: "
-                   "want 5 or 6 elements, have %d", p->fn,
-                   sk_ASN1_TYPE_num(seq));
-               goto out;
-       }
-
        /* Parse the optional version field */
-       if (sk_ASN1_TYPE_num(seq) == 6) {
-               t = sk_ASN1_TYPE_value(seq, i++);
-               d = t->value.asn1_string->data;
-               dsz = t->value.asn1_string->length;
-
-               if (cms_econtent_version(p->fn, &d, dsz, &mft_version) == -1)
+       if (mft->version != NULL) {
+               mft_version = ASN1_INTEGER_get(mft->version);
+               if (mft_version < 0) {
+                       cryptowarnx("%s: ASN1_INTEGER_get failed", p->fn);
                        goto out;
+               }
 
                switch (mft_version) {
                case 0:
@@ -354,17 +301,7 @@ mft_parse_econtent(const unsigned char *d, size_t dsz, struct parse *p)
                }
        }
 
-       /* Now the manifest sequence number. */
-
-       t = sk_ASN1_TYPE_value(seq, i++);
-       if (t->type != V_ASN1_INTEGER) {
-               warnx("%s: RFC 6486 section 4.2.1: manifestNumber: "
-                   "want ASN.1 integer, have %s (NID %d)",
-                   p->fn, ASN1_tag2str(t->type), t->type);
-               goto out;
-       }
-
-       p->res->seqnum = x509_convert_seqnum(p->fn, t->value.integer);
+       p->res->seqnum = x509_convert_seqnum(p->fn, mft->manifestNumber);
        if (p->res->seqnum == NULL)
                goto out;
 
@@ -378,60 +315,46 @@ mft_parse_econtent(const unsigned char *d, size_t dsz, struct parse *p)
         * compare against the current time trivially.
         */
 
-       t = sk_ASN1_TYPE_value(seq, i++);
-       if (t->type != V_ASN1_GENERALIZEDTIME) {
-               warnx("%s: RFC 6486 section 4.2.1: thisUpdate: "
-                   "want ASN.1 generalised time, have %s (NID %d)",
-                   p->fn, ASN1_tag2str(t->type), t->type);
-               goto out;
-       }
-       from = t->value.generalizedtime;
-
-       t = sk_ASN1_TYPE_value(seq, i++);
-       if (t->type != V_ASN1_GENERALIZEDTIME) {
-               warnx("%s: RFC 6486 section 4.2.1: nextUpdate: "
-                   "want ASN.1 generalised time, have %s (NID %d)",
-                   p->fn, ASN1_tag2str(t->type), t->type);
-               goto out;
-       }
-       until = t->value.generalizedtime;
-
-       if (!mft_parse_time(from, until, p))
+       if (!mft_parse_time(mft->thisUpdate, mft->nextUpdate, p))
                goto out;
 
        /* File list algorithm. */
 
-       t = sk_ASN1_TYPE_value(seq, i++);
-       if (t->type != V_ASN1_OBJECT) {
-               warnx("%s: RFC 6486 section 4.2.1: fileHashAlg: "
-                   "want ASN.1 object, have %s (NID %d)",
-                   p->fn, ASN1_tag2str(t->type), t->type);
-               goto out;
-       }
-       if (OBJ_obj2nid(t->value.object) != NID_sha256) {
+       if (OBJ_obj2nid(mft->fileHashAlg) != NID_sha256) {
                warnx("%s: RFC 6486 section 4.2.1: fileHashAlg: "
                    "want SHA256 object, have %s (NID %d)", p->fn,
-                   ASN1_tag2str(OBJ_obj2nid(t->value.object)),
-                   OBJ_obj2nid(t->value.object));
+                   ASN1_tag2str(OBJ_obj2nid(mft->fileHashAlg)),
+                   OBJ_obj2nid(mft->fileHashAlg));
                goto out;
        }
 
        /* Now the sequence. */
 
-       t = sk_ASN1_TYPE_value(seq, i++);
-       if (t->type != V_ASN1_SEQUENCE) {
-               warnx("%s: RFC 6486 section 4.2.1: fileList: "
-                   "want ASN.1 sequence, have %s (NID %d)",
-                   p->fn, ASN1_tag2str(t->type), t->type);
+       if (sk_FileAndHash_num(mft->fileList) > MAX_MANIFEST_ENTRIES) {
+               warnx("%s: %d exceeds manifest entry limit (%d)", p->fn,
+                   sk_FileAndHash_num(mft->fileList), MAX_MANIFEST_ENTRIES);
                goto out;
        }
 
-       if (!mft_parse_flist(p, t->value.octet_string))
+       p->res->files = calloc(sk_FileAndHash_num(mft->fileList),
+           sizeof(struct mftfile));
+       if (p->res->files == NULL)
+               err(1, NULL);
+
+       for (i = 0; i < sk_FileAndHash_num(mft->fileList); i++) {
+               fh = sk_FileAndHash_value(mft->fileList, i);
+               if (!mft_parse_filehash(p, fh))
+                       goto out;
+       }
+
+       if (!p->found_crl) {
+               warnx("%s: CRL not part of MFT fileList", p->fn);
                goto out;
+       }
 
        rc = 1;
-out:
-       sk_ASN1_TYPE_pop_free(seq, ASN1_TYPE_free);
+ out:
+       Manifest_free(mft);
        return rc;
 }
 
index a03efc6..56360ef 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: roa.c,v 1.42 2022/05/15 16:43:35 tb Exp $ */
+/*     $OpenBSD: roa.c,v 1.43 2022/05/19 06:37:51 tb Exp $ */
 /*
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
  *
@@ -24,6 +24,9 @@
 #include <unistd.h>
 
 #include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/stack.h>
+#include <openssl/safestack.h>
 #include <openssl/x509.h>
 
 #include "extern.h"
@@ -39,214 +42,57 @@ struct     parse {
 extern ASN1_OBJECT     *roa_oid;
 
 /*
- * Parse IP address (ROAIPAddress), RFC 6482, section 3.3.
- * Returns zero on failure, non-zero on success.
+ * Types and templates for the ROA eContent, RFC 6482, section 3.
  */
-static int
-roa_parse_addr(const ASN1_OCTET_STRING *os, enum afi afi, struct parse *p)
-{
-       ASN1_SEQUENCE_ANY       *seq;
-       const unsigned char     *d = os->data;
-       size_t                   dsz = os->length;
-       int                      rc = 0;
-       const ASN1_TYPE         *t;
-       const ASN1_INTEGER      *maxlength;
-       long                     maxlen;
-       struct ip_addr           addr;
-       struct roa_ip           *res;
-
-       if ((seq = d2i_ASN1_SEQUENCE_ANY(NULL, &d, dsz)) == NULL) {
-               cryptowarnx("%s: RFC 6482 section 3.3: address: "
-                   "failed ASN.1 sequence parse", p->fn);
-               goto out;
-       }
-
-       /* ROAIPAddress has the address and optional maxlength. */
-
-       if (sk_ASN1_TYPE_num(seq) != 1 &&
-           sk_ASN1_TYPE_num(seq) != 2) {
-               warnx("%s: RFC 6482 section 3.3: address: "
-                   "want 1 or 2 elements, have %d",
-                   p->fn, sk_ASN1_TYPE_num(seq));
-               goto out;
-       }
-
-       t = sk_ASN1_TYPE_value(seq, 0);
-       if (t->type != V_ASN1_BIT_STRING) {
-               warnx("%s: RFC 6482 section 3.3: address: "
-                   "want ASN.1 bit string, have %s (NID %d)",
-                   p->fn, ASN1_tag2str(t->type), t->type);
-               goto out;
-       }
-       if (!ip_addr_parse(t->value.bit_string, afi, p->fn, &addr)) {
-               warnx("%s: RFC 6482 section 3.3: address: "
-                   "invalid IP address", p->fn);
-               goto out;
-       }
-       maxlen = addr.prefixlen;
-
-       if (sk_ASN1_TYPE_num(seq) == 2) {
-               t = sk_ASN1_TYPE_value(seq, 1);
-               if (t->type != V_ASN1_INTEGER) {
-                       warnx("%s: RFC 6482 section 3.1: maxLength: "
-                           "want ASN.1 integer, have %s (NID %d)",
-                           p->fn, ASN1_tag2str(t->type), t->type);
-                       goto out;
-               }
-
-               maxlength = t->value.integer;
-               maxlen = ASN1_INTEGER_get(maxlength);
-               if (maxlen < 0) {
-                       warnx("%s: RFC 6482 section 3.2: maxLength: "
-                           "want positive integer, have %ld", p->fn, maxlen);
-                       goto out;
-               }
-               if (addr.prefixlen > maxlen) {
-                       warnx("%s: prefixlen (%d) larger than maxLength (%ld)",
-                           p->fn, addr.prefixlen, maxlen);
-                       goto out;
-               }
-               if (maxlen > ((afi == AFI_IPV4) ? 32 : 128)) {
-                       warnx("%s: maxLength (%ld) too large", p->fn, maxlen);
-                       goto out;
-               }
-       }
-
-       res = &p->res->ips[p->res->ipsz++];
 
-       res->addr = addr;
-       res->afi = afi;
-       res->maxlength = maxlen;
-       ip_roa_compose_ranges(res);
+typedef struct {
+       ASN1_BIT_STRING         *address;
+       ASN1_INTEGER            *maxLength;
+} ROAIPAddress;
 
-       rc = 1;
-out:
-       sk_ASN1_TYPE_pop_free(seq, ASN1_TYPE_free);
-       return rc;
-}
+DECLARE_STACK_OF(ROAIPAddress);
 
-/*
- * Parse IP address family, RFC 6482, section 3.3.
- * Returns zero on failure, non-zero on success.
- */
-static int
-roa_parse_ipfam(const ASN1_OCTET_STRING *os, struct parse *p)
-{
-       ASN1_SEQUENCE_ANY       *seq, *sseq = NULL;
-       const unsigned char     *d = os->data;
-       size_t                   dsz = os->length;
-       int                      i, rc = 0;
-       const ASN1_TYPE         *t;
-       enum afi                 afi;
-
-       if ((seq = d2i_ASN1_SEQUENCE_ANY(NULL, &d, dsz)) == NULL) {
-               cryptowarnx("%s: RFC 6482 section 3.3: ROAIPAddressFamily: "
-                   "failed ASN.1 sequence parse", p->fn);
-               goto out;
-       }
-       if (sk_ASN1_TYPE_num(seq) != 2) {
-               warnx("%s: RFC 6482 section 3.3: ROAIPAddressFamily: "
-                   "want 2 elements, have %d",
-                   p->fn, sk_ASN1_TYPE_num(seq));
-               goto out;
-       }
+typedef struct {
+       ASN1_OCTET_STRING       *addressFamily;
+       STACK_OF(ROAIPAddress)  *addresses;
+} ROAIPAddressFamily;
 
-       t = sk_ASN1_TYPE_value(seq, 0);
-       if (t->type != V_ASN1_OCTET_STRING) {
-               warnx("%s: RFC 6482 section 3.3: addressFamily: "
-                   "want ASN.1 octet string, have %s (NID %d)",
-                   p->fn, ASN1_tag2str(t->type), t->type);
-               goto out;
-       }
-       if (!ip_addr_afi_parse(p->fn, t->value.octet_string, &afi)) {
-               warnx("%s: RFC 6482 section 3.3: addressFamily: "
-                   "invalid", p->fn);
-               goto out;
-       }
-
-       t = sk_ASN1_TYPE_value(seq, 1);
-       if (t->type != V_ASN1_SEQUENCE) {
-               warnx("%s: RFC 6482 section 3.3: addresses: "
-                   "want ASN.1 sequence, have %s (NID %d)",
-                   p->fn, ASN1_tag2str(t->type), t->type);
-               goto out;
-       }
-
-       d = t->value.octet_string->data;
-       dsz = t->value.octet_string->length;
+DECLARE_STACK_OF(ROAIPAddressFamily);
 
-       if ((sseq = d2i_ASN1_SEQUENCE_ANY(NULL, &d, dsz)) == NULL) {
-               cryptowarnx("%s: RFC 6482 section 3.3: addresses: "
-                   "failed ASN.1 sequence parse", p->fn);
-               goto out;
-       }
+#if defined(LIBRESSL_VERSION_NUMBER)
+#define sk_ROAIPAddress_num(st)                SKM_sk_num(ROAIPAddress, (st))
+#define sk_ROAIPAddress_value(st, i)   SKM_sk_value(ROAIPAddress, (st), (i))
 
-       /* will be called multiple times so use recallocarray */
-       if (p->res->ipsz + sk_ASN1_TYPE_num(sseq) >= MAX_IP_SIZE) {
-               warnx("%s: too many IPAddress entries: limit %d",
-                   p->fn, MAX_IP_SIZE);
-               goto out;
-       }
-       p->res->ips = recallocarray(p->res->ips, p->res->ipsz,
-           p->res->ipsz + sk_ASN1_TYPE_num(sseq), sizeof(struct roa_ip));
-       if (p->res->ips == NULL)
-               err(1, NULL);
+#define sk_ROAIPAddressFamily_num(st)  SKM_sk_num(ROAIPAddressFamily, (st))
+#define sk_ROAIPAddressFamily_value(st, i) \
+    SKM_sk_value(ROAIPAddressFamily, (st), (i))
+#endif
 
-       for (i = 0; i < sk_ASN1_TYPE_num(sseq); i++) {
-               t = sk_ASN1_TYPE_value(sseq, i);
-               if (t->type != V_ASN1_SEQUENCE) {
-                       warnx("%s: RFC 6482 section 3.3: ROAIPAddress: "
-                           "want ASN.1 sequence, have %s (NID %d)",
-                           p->fn, ASN1_tag2str(t->type), t->type);
-                       goto out;
-               }
-               if (!roa_parse_addr(t->value.octet_string, afi, p))
-                       goto out;
-       }
+typedef struct {
+       ASN1_INTEGER                    *version;
+       ASN1_INTEGER                    *asid;
+       STACK_OF(ROAIPAddressFamily)    *ipAddrBlocks;
+} RouteOriginAttestation;
 
-       rc = 1;
-out:
-       sk_ASN1_TYPE_pop_free(seq, ASN1_TYPE_free);
-       sk_ASN1_TYPE_pop_free(sseq, ASN1_TYPE_free);
-       return rc;
-}
+ASN1_SEQUENCE(ROAIPAddress) = {
+       ASN1_SIMPLE(ROAIPAddress, address, ASN1_BIT_STRING),
+       ASN1_OPT(ROAIPAddress, maxLength, ASN1_INTEGER),
+} ASN1_SEQUENCE_END(ROAIPAddress);
 
-/*
- * Parse IP blocks, RFC 6482, section 3.3.
- * Returns zero on failure, non-zero on success.
- */
-static int
-roa_parse_ipblocks(const ASN1_OCTET_STRING *os, struct parse *p)
-{
-       ASN1_SEQUENCE_ANY       *seq;
-       const unsigned char     *d = os->data;
-       size_t                   dsz = os->length;
-       int                      i, rc = 0;
-       const ASN1_TYPE         *t;
-
-       if ((seq = d2i_ASN1_SEQUENCE_ANY(NULL, &d, dsz)) == NULL) {
-               cryptowarnx("%s: RFC 6482 section 3.3: ipAddrBlocks: "
-                   "failed ASN.1 sequence parse", p->fn);
-               goto out;
-       }
+ASN1_SEQUENCE(ROAIPAddressFamily) = {
+       ASN1_SIMPLE(ROAIPAddressFamily, addressFamily, ASN1_OCTET_STRING),
+       ASN1_SEQUENCE_OF(ROAIPAddressFamily, addresses, ROAIPAddress),
+} ASN1_SEQUENCE_END(ROAIPAddressFamily);
 
-       for (i = 0; i < sk_ASN1_TYPE_num(seq); i++) {
-               t = sk_ASN1_TYPE_value(seq, i);
-               if (t->type != V_ASN1_SEQUENCE) {
-                       warnx("%s: RFC 6482 section 3.3: ROAIPAddressFamily: "
-                           "want ASN.1 sequence, have %s (NID %d)",
-                           p->fn, ASN1_tag2str(t->type), t->type);
-                       goto out;
-               }
-               if (!roa_parse_ipfam(t->value.octet_string, p))
-                       goto out;
-       }
+ASN1_SEQUENCE(RouteOriginAttestation) = {
+       ASN1_IMP_OPT(RouteOriginAttestation, version, ASN1_INTEGER, 0),
+       ASN1_SIMPLE(RouteOriginAttestation, asid, ASN1_INTEGER),
+       ASN1_SEQUENCE_OF(RouteOriginAttestation, ipAddrBlocks,
+           ROAIPAddressFamily),
+} ASN1_SEQUENCE_END(RouteOriginAttestation);
 
-       rc = 1;
-out:
-       sk_ASN1_TYPE_pop_free(seq, ASN1_TYPE_free);
-       return rc;
-}
+DECLARE_ASN1_FUNCTIONS(RouteOriginAttestation);
+IMPLEMENT_ASN1_FUNCTIONS(RouteOriginAttestation);
 
 /*
  * Parses the eContent section of an ROA file, RFC 6482, section 3.
@@ -255,34 +101,31 @@ out:
 static int
 roa_parse_econtent(const unsigned char *d, size_t dsz, struct parse *p)
 {
-       ASN1_SEQUENCE_ANY       *seq;
-       int                      i = 0, rc = 0, sz;
-       const ASN1_TYPE         *t;
-       long                     roa_version;
-
-       /* RFC 6482, section 3. */
-
-       if ((seq = d2i_ASN1_SEQUENCE_ANY(NULL, &d, dsz)) == NULL) {
-               cryptowarnx("%s: RFC 6482 section 3: RouteOriginAttestation: "
-                   "failed ASN.1 sequence parse", p->fn);
-               goto out;
-       }
-
-       if ((sz = sk_ASN1_TYPE_num(seq)) != 2 && sz != 3) {
-               warnx("%s: RFC 6482 section 3: RouteOriginAttestation: "
-                   "want 2 or 3 elements, have %d",
-                   p->fn, sk_ASN1_TYPE_num(seq));
+       RouteOriginAttestation          *roa;
+       long                             roa_version;
+       const ROAIPAddressFamily        *addrfam;
+       const STACK_OF(ROAIPAddress)    *addrs;
+       int                              addrsz;
+       enum afi                         afi;
+       const ROAIPAddress              *addr;
+       long                             maxlen;
+       struct ip_addr                   ipaddr;
+       struct roa_ip                   *res;
+       int                              i, j, rc = 0;
+
+       if ((roa = d2i_RouteOriginAttestation(NULL, &d, dsz)) == NULL) {
+               cryptowarnx("%s: RFC 6482 section 3: failed to parse "
+                   "RouteOriginAttestation", p->fn);
                goto out;
        }
 
        /* Parse the optional version field */
-       if (sz == 3) {
-               t = sk_ASN1_TYPE_value(seq, i++);
-               d = t->value.asn1_string->data;
-               dsz = t->value.asn1_string->length;
-
-               if (cms_econtent_version(p->fn, &d, dsz, &roa_version) == -1)
+       if (roa->version != NULL) {
+               roa_version = ASN1_INTEGER_get(roa->version);
+               if (roa_version < 0) {
+                       warnx("%s: ASN1_INTEGER_get failed", p->fn);
                        goto out;
+               }
 
                switch (roa_version) {
                case 0:
@@ -295,40 +138,75 @@ roa_parse_econtent(const unsigned char *d, size_t dsz, struct parse *p)
                }
        }
 
-       /*
-        * RFC 6482, section 3.2.
-        * It doesn't ever actually state that AS numbers can't be
-        * negative, but...?
-        */
-
-       t = sk_ASN1_TYPE_value(seq, i++);
-       if (t->type != V_ASN1_INTEGER) {
-               warnx("%s: RFC 6482 section 3.2: asID: "
-                   "want ASN.1 integer, have %s (NID %d)",
-                   p->fn, ASN1_tag2str(t->type), t->type);
-               goto out;
-       }
-       if (!as_id_parse(t->value.integer, &p->res->asid)) {
+       if (!as_id_parse(roa->asid, &p->res->asid)) {
                warnx("%s: RFC 6482 section 3.2: asID: "
                    "malformed AS identifier", p->fn);
                goto out;
        }
 
-       /* RFC 6482, section 3.3. */
+       for (i = 0; i < sk_ROAIPAddressFamily_num(roa->ipAddrBlocks); i++) {
+               addrfam = sk_ROAIPAddressFamily_value(roa->ipAddrBlocks, i);
+               addrs = addrfam->addresses;
+               addrsz = sk_ROAIPAddress_num(addrs);
 
-       t = sk_ASN1_TYPE_value(seq, i++);
-       if (t->type != V_ASN1_SEQUENCE) {
-               warnx("%s: RFC 6482 section 3.3: ipAddrBlocks: "
-                   "want ASN.1 sequence, have %s (NID %d)",
-                   p->fn, ASN1_tag2str(t->type), t->type);
-               goto out;
+               if (!ip_addr_afi_parse(p->fn, addrfam->addressFamily, &afi)) {
+                       warnx("%s: RFC 6482 section 3.3: addressFamily: "
+                           "invalid", p->fn);
+                       goto out;
+               }
+
+               if (p->res->ipsz + addrsz >= MAX_IP_SIZE) {
+                       warnx("%s: too many IPAddress entries: limit %d",
+                           p->fn, MAX_IP_SIZE);
+                       goto out;
+               }
+               p->res->ips = recallocarray(p->res->ips, p->res->ipsz,
+                   p->res->ipsz + addrsz, sizeof(struct roa_ip));
+               if (p->res->ips == NULL)
+                       err(1, NULL);
+
+               for (j = 0; j < addrsz; j++) {
+                       addr = sk_ROAIPAddress_value(addrs, j);
+
+                       if (!ip_addr_parse(addr->address, afi, p->fn,
+                           &ipaddr)) {
+                               warnx("%s: RFC 6482 section 3.3: address: "
+                                   "invalid IP address", p->fn);
+                               goto out;
+                       }
+                       maxlen = ipaddr.prefixlen;
+
+                       if (addr->maxLength != NULL) {
+                               maxlen = ASN1_INTEGER_get(addr->maxLength);
+                               if (maxlen < 0) {
+                                       warnx("%s: RFC 6482 section 3.2: "
+                                           "ASN1_INTEGER_get failed", p->fn);
+                                       goto out;
+                               }
+                               if (ipaddr.prefixlen > maxlen) {
+                                       warnx("%s: prefixlen (%d) larger than "
+                                           "maxLength (%ld)", p->fn,
+                                           ipaddr.prefixlen, maxlen);
+                                       goto out;
+                               }
+                               if (maxlen > ((afi == AFI_IPV4) ? 32 : 128)) {
+                                       warnx("%s: maxLength (%ld) too large",
+                                           p->fn, maxlen);
+                                       goto out;
+                               }
+                       }
+
+                       res = &p->res->ips[p->res->ipsz++];
+                       res->addr = ipaddr;
+                       res->afi = afi;
+                       res->maxlength = maxlen;
+                       ip_roa_compose_ranges(res);
+               }
        }
-       if (!roa_parse_ipblocks(t->value.octet_string, p))
-               goto out;
 
        rc = 1;
-out:
-       sk_ASN1_TYPE_pop_free(seq, ASN1_TYPE_free);
+ out:
+       RouteOriginAttestation_free(roa);
        return rc;
 }