From: tb Date: Thu, 19 May 2022 06:37:51 +0000 (+0000) Subject: Rewrite mft.c and roa.c with ASN.1 templates X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=23e50b687b417416b07e204e0f151b8a5c0ce10b;p=openbsd Rewrite mft.c and roa.c with ASN.1 templates 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 --- diff --git a/usr.sbin/rpki-client/mft.c b/usr.sbin/rpki-client/mft.c index eb8e4691776..cf7ab80188a 100644 --- a/usr.sbin/rpki-client/mft.c +++ b/usr.sbin/rpki-client/mft.c @@ -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 * @@ -27,7 +27,10 @@ #include #include +#include +#include #include +#include #include #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; } diff --git a/usr.sbin/rpki-client/roa.c b/usr.sbin/rpki-client/roa.c index a03efc6086f..56360efd37d 100644 --- a/usr.sbin/rpki-client/roa.c +++ b/usr.sbin/rpki-client/roa.c @@ -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 * @@ -24,6 +24,9 @@ #include #include +#include +#include +#include #include #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; }