Lay groundwork to support X.509 v3 extensions for IP Addresses and AS Identifiers
authorjob <job@openbsd.org>
Thu, 2 Sep 2021 12:41:44 +0000 (12:41 +0000)
committerjob <job@openbsd.org>
Thu, 2 Sep 2021 12:41:44 +0000 (12:41 +0000)
These extensions are defined in RFC 3779 and used in the RPKI (RFC 6482, RFC 8360).
Imported from OpenSSL 1.1.1j (aaf2fcb575cdf6491b98ab4829abf78a3dec8402b8b81efc8f23c00d443981bf)
This changeset is a no-op, as there are 10+ issues and at least 2 security issues.
Work will continue in-tree.

OK tb@, discussed with beck@

lib/libcrypto/asn1/x_x509.c
lib/libcrypto/x509/ext_dat.h
lib/libcrypto/x509/x509.h
lib/libcrypto/x509/x509_addr.c [new file with mode: 0644]
lib/libcrypto/x509/x509_asid.c [new file with mode: 0644]
lib/libcrypto/x509/x509_purp.c
lib/libcrypto/x509/x509v3.h

index 6a56a79..422f625 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: x_x509.c,v 1.26 2018/02/17 15:50:42 jsing Exp $ */
+/* $OpenBSD: x_x509.c,v 1.27 2021/09/02 12:41:44 job Exp $ */
 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
  * All rights reserved.
  *
@@ -185,6 +185,10 @@ x509_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, void *exarg)
                ret->akid = NULL;
                ret->aux = NULL;
                ret->crldp = NULL;
+#ifndef OPENSSL_NO_RFC3779
+               ret->rfc3779_addr = NULL;
+               ret->rfc3779_asid = NULL;
+#endif
                CRYPTO_new_ex_data(CRYPTO_EX_INDEX_X509, ret, &ret->ex_data);
                break;
 
@@ -202,6 +206,10 @@ x509_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, void *exarg)
                policy_cache_free(ret->policy_cache);
                GENERAL_NAMES_free(ret->altname);
                NAME_CONSTRAINTS_free(ret->nc);
+#ifndef OPENSSL_NO_RFC3779
+               sk_IPAddressFamily_pop_free(ret->rfc3779_addr, IPAddressFamily_free);
+               ASIdentifiers_free(ret->rfc3779_asid);
+#endif
                free(ret->name);
                ret->name = NULL;
                break;
index 1a7ae6e..53f8eb9 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ext_dat.h,v 1.1 2020/06/04 15:19:31 jsing Exp $ */
+/* $OpenBSD: ext_dat.h,v 1.2 2021/09/02 12:41:44 job Exp $ */
 /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
  * project 1999.
  */
@@ -105,6 +105,10 @@ static const X509V3_EXT_METHOD *standard_exts[] = {
 #endif
        &v3_sxnet,
        &v3_info,
+#ifndef OPENSSL_NO_RFC3779
+       &v3_addr,
+       &v3_asid,
+#endif
 #ifndef OPENSSL_NO_OCSP
        &v3_ocsp_nonce,
        &v3_ocsp_crlid,
index f135ee9..5b25c6e 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: x509.h,v 1.75 2021/03/31 16:51:06 tb Exp $ */
+/* $OpenBSD: x509.h,v 1.76 2021/09/02 12:41:44 job Exp $ */
 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
  * All rights reserved.
  *
@@ -287,6 +287,10 @@ struct x509_st
        STACK_OF(DIST_POINT) *crldp;
        STACK_OF(GENERAL_NAME) *altname;
        NAME_CONSTRAINTS *nc;
+#ifndef OPENSSL_NO_RFC3779
+       STACK_OF(IPAddressFamily) *rfc3779_addr;
+       struct ASIdentifiers_st *rfc3779_asid;
+#endif
 #ifndef OPENSSL_NO_SHA
        unsigned char sha1_hash[SHA_DIGEST_LENGTH];
 #endif
diff --git a/lib/libcrypto/x509/x509_addr.c b/lib/libcrypto/x509/x509_addr.c
new file mode 100644 (file)
index 0000000..4258dbc
--- /dev/null
@@ -0,0 +1,1315 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Implementation of RFC 3779 section 2.2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "internal/cryptlib.h"
+#include <openssl/conf.h>
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/buffer.h>
+#include <openssl/x509v3.h>
+#include "crypto/x509.h"
+#include "ext_dat.h"
+
+#ifndef OPENSSL_NO_RFC3779
+
+/*
+ * OpenSSL ASN.1 template translation of RFC 3779 2.2.3.
+ */
+
+ASN1_SEQUENCE(IPAddressRange) = {
+  ASN1_SIMPLE(IPAddressRange, min, ASN1_BIT_STRING),
+  ASN1_SIMPLE(IPAddressRange, max, ASN1_BIT_STRING)
+} ASN1_SEQUENCE_END(IPAddressRange)
+
+ASN1_CHOICE(IPAddressOrRange) = {
+  ASN1_SIMPLE(IPAddressOrRange, u.addressPrefix, ASN1_BIT_STRING),
+  ASN1_SIMPLE(IPAddressOrRange, u.addressRange,  IPAddressRange)
+} ASN1_CHOICE_END(IPAddressOrRange)
+
+ASN1_CHOICE(IPAddressChoice) = {
+  ASN1_SIMPLE(IPAddressChoice,      u.inherit,           ASN1_NULL),
+  ASN1_SEQUENCE_OF(IPAddressChoice, u.addressesOrRanges, IPAddressOrRange)
+} ASN1_CHOICE_END(IPAddressChoice)
+
+ASN1_SEQUENCE(IPAddressFamily) = {
+  ASN1_SIMPLE(IPAddressFamily, addressFamily,   ASN1_OCTET_STRING),
+  ASN1_SIMPLE(IPAddressFamily, ipAddressChoice, IPAddressChoice)
+} ASN1_SEQUENCE_END(IPAddressFamily)
+
+ASN1_ITEM_TEMPLATE(IPAddrBlocks) =
+  ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0,
+                        IPAddrBlocks, IPAddressFamily)
+static_ASN1_ITEM_TEMPLATE_END(IPAddrBlocks)
+
+IMPLEMENT_ASN1_FUNCTIONS(IPAddressRange)
+IMPLEMENT_ASN1_FUNCTIONS(IPAddressOrRange)
+IMPLEMENT_ASN1_FUNCTIONS(IPAddressChoice)
+IMPLEMENT_ASN1_FUNCTIONS(IPAddressFamily)
+
+/*
+ * How much buffer space do we need for a raw address?
+ */
+#define ADDR_RAW_BUF_LEN        16
+
+/*
+ * What's the address length associated with this AFI?
+ */
+static int length_from_afi(const unsigned afi)
+{
+    switch (afi) {
+    case IANA_AFI_IPV4:
+        return 4;
+    case IANA_AFI_IPV6:
+        return 16;
+    default:
+        return 0;
+    }
+}
+
+/*
+ * Extract the AFI from an IPAddressFamily.
+ */
+unsigned int X509v3_addr_get_afi(const IPAddressFamily *f)
+{
+    if (f == NULL
+            || f->addressFamily == NULL
+            || f->addressFamily->data == NULL
+            || f->addressFamily->length < 2)
+        return 0;
+    return (f->addressFamily->data[0] << 8) | f->addressFamily->data[1];
+}
+
+/*
+ * Expand the bitstring form of an address into a raw byte array.
+ * At the moment this is coded for simplicity, not speed.
+ */
+static int addr_expand(unsigned char *addr,
+                       const ASN1_BIT_STRING *bs,
+                       const int length, const unsigned char fill)
+{
+    if (bs->length < 0 || bs->length > length)
+        return 0;
+    if (bs->length > 0) {
+        memcpy(addr, bs->data, bs->length);
+        if ((bs->flags & 7) != 0) {
+            unsigned char mask = 0xFF >> (8 - (bs->flags & 7));
+            if (fill == 0)
+                addr[bs->length - 1] &= ~mask;
+            else
+                addr[bs->length - 1] |= mask;
+        }
+    }
+    memset(addr + bs->length, fill, length - bs->length);
+    return 1;
+}
+
+/*
+ * Extract the prefix length from a bitstring.
+ */
+#define addr_prefixlen(bs) ((int) ((bs)->length * 8 - ((bs)->flags & 7)))
+
+/*
+ * i2r handler for one address bitstring.
+ */
+static int i2r_address(BIO *out,
+                       const unsigned afi,
+                       const unsigned char fill, const ASN1_BIT_STRING *bs)
+{
+    unsigned char addr[ADDR_RAW_BUF_LEN];
+    int i, n;
+
+    if (bs->length < 0)
+        return 0;
+    switch (afi) {
+    case IANA_AFI_IPV4:
+        if (!addr_expand(addr, bs, 4, fill))
+            return 0;
+        BIO_printf(out, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]);
+        break;
+    case IANA_AFI_IPV6:
+        if (!addr_expand(addr, bs, 16, fill))
+            return 0;
+        for (n = 16; n > 1 && addr[n - 1] == 0x00 && addr[n - 2] == 0x00;
+             n -= 2) ;
+        for (i = 0; i < n; i += 2)
+            BIO_printf(out, "%x%s", (addr[i] << 8) | addr[i + 1],
+                       (i < 14 ? ":" : ""));
+        if (i < 16)
+            BIO_puts(out, ":");
+        if (i == 0)
+            BIO_puts(out, ":");
+        break;
+    default:
+        for (i = 0; i < bs->length; i++)
+            BIO_printf(out, "%s%02x", (i > 0 ? ":" : ""), bs->data[i]);
+        BIO_printf(out, "[%d]", (int)(bs->flags & 7));
+        break;
+    }
+    return 1;
+}
+
+/*
+ * i2r handler for a sequence of addresses and ranges.
+ */
+static int i2r_IPAddressOrRanges(BIO *out,
+                                 const int indent,
+                                 const IPAddressOrRanges *aors,
+                                 const unsigned afi)
+{
+    int i;
+    for (i = 0; i < sk_IPAddressOrRange_num(aors); i++) {
+        const IPAddressOrRange *aor = sk_IPAddressOrRange_value(aors, i);
+        BIO_printf(out, "%*s", indent, "");
+        switch (aor->type) {
+        case IPAddressOrRange_addressPrefix:
+            if (!i2r_address(out, afi, 0x00, aor->u.addressPrefix))
+                return 0;
+            BIO_printf(out, "/%d\n", addr_prefixlen(aor->u.addressPrefix));
+            continue;
+        case IPAddressOrRange_addressRange:
+            if (!i2r_address(out, afi, 0x00, aor->u.addressRange->min))
+                return 0;
+            BIO_puts(out, "-");
+            if (!i2r_address(out, afi, 0xFF, aor->u.addressRange->max))
+                return 0;
+            BIO_puts(out, "\n");
+            continue;
+        }
+    }
+    return 1;
+}
+
+/*
+ * i2r handler for an IPAddrBlocks extension.
+ */
+static int i2r_IPAddrBlocks(const X509V3_EXT_METHOD *method,
+                            void *ext, BIO *out, int indent)
+{
+    const IPAddrBlocks *addr = ext;
+    int i;
+    for (i = 0; i < sk_IPAddressFamily_num(addr); i++) {
+        IPAddressFamily *f = sk_IPAddressFamily_value(addr, i);
+        const unsigned int afi = X509v3_addr_get_afi(f);
+        switch (afi) {
+        case IANA_AFI_IPV4:
+            BIO_printf(out, "%*sIPv4", indent, "");
+            break;
+        case IANA_AFI_IPV6:
+            BIO_printf(out, "%*sIPv6", indent, "");
+            break;
+        default:
+            BIO_printf(out, "%*sUnknown AFI %u", indent, "", afi);
+            break;
+        }
+        if (f->addressFamily->length > 2) {
+            switch (f->addressFamily->data[2]) {
+            case 1:
+                BIO_puts(out, " (Unicast)");
+                break;
+            case 2:
+                BIO_puts(out, " (Multicast)");
+                break;
+            case 3:
+                BIO_puts(out, " (Unicast/Multicast)");
+                break;
+            case 4:
+                BIO_puts(out, " (MPLS)");
+                break;
+            case 64:
+                BIO_puts(out, " (Tunnel)");
+                break;
+            case 65:
+                BIO_puts(out, " (VPLS)");
+                break;
+            case 66:
+                BIO_puts(out, " (BGP MDT)");
+                break;
+            case 128:
+                BIO_puts(out, " (MPLS-labeled VPN)");
+                break;
+            default:
+                BIO_printf(out, " (Unknown SAFI %u)",
+                           (unsigned)f->addressFamily->data[2]);
+                break;
+            }
+        }
+        switch (f->ipAddressChoice->type) {
+        case IPAddressChoice_inherit:
+            BIO_puts(out, ": inherit\n");
+            break;
+        case IPAddressChoice_addressesOrRanges:
+            BIO_puts(out, ":\n");
+            if (!i2r_IPAddressOrRanges(out,
+                                       indent + 2,
+                                       f->ipAddressChoice->
+                                       u.addressesOrRanges, afi))
+                return 0;
+            break;
+        }
+    }
+    return 1;
+}
+
+/*
+ * Sort comparison function for a sequence of IPAddressOrRange
+ * elements.
+ *
+ * There's no sane answer we can give if addr_expand() fails, and an
+ * assertion failure on externally supplied data is seriously uncool,
+ * so we just arbitrarily declare that if given invalid inputs this
+ * function returns -1.  If this messes up your preferred sort order
+ * for garbage input, tough noogies.
+ */
+static int IPAddressOrRange_cmp(const IPAddressOrRange *a,
+                                const IPAddressOrRange *b, const int length)
+{
+    unsigned char addr_a[ADDR_RAW_BUF_LEN], addr_b[ADDR_RAW_BUF_LEN];
+    int prefixlen_a = 0, prefixlen_b = 0;
+    int r;
+
+    switch (a->type) {
+    case IPAddressOrRange_addressPrefix:
+        if (!addr_expand(addr_a, a->u.addressPrefix, length, 0x00))
+            return -1;
+        prefixlen_a = addr_prefixlen(a->u.addressPrefix);
+        break;
+    case IPAddressOrRange_addressRange:
+        if (!addr_expand(addr_a, a->u.addressRange->min, length, 0x00))
+            return -1;
+        prefixlen_a = length * 8;
+        break;
+    }
+
+    switch (b->type) {
+    case IPAddressOrRange_addressPrefix:
+        if (!addr_expand(addr_b, b->u.addressPrefix, length, 0x00))
+            return -1;
+        prefixlen_b = addr_prefixlen(b->u.addressPrefix);
+        break;
+    case IPAddressOrRange_addressRange:
+        if (!addr_expand(addr_b, b->u.addressRange->min, length, 0x00))
+            return -1;
+        prefixlen_b = length * 8;
+        break;
+    }
+
+    if ((r = memcmp(addr_a, addr_b, length)) != 0)
+        return r;
+    else
+        return prefixlen_a - prefixlen_b;
+}
+
+/*
+ * IPv4-specific closure over IPAddressOrRange_cmp, since sk_sort()
+ * comparison routines are only allowed two arguments.
+ */
+static int v4IPAddressOrRange_cmp(const IPAddressOrRange *const *a,
+                                  const IPAddressOrRange *const *b)
+{
+    return IPAddressOrRange_cmp(*a, *b, 4);
+}
+
+/*
+ * IPv6-specific closure over IPAddressOrRange_cmp, since sk_sort()
+ * comparison routines are only allowed two arguments.
+ */
+static int v6IPAddressOrRange_cmp(const IPAddressOrRange *const *a,
+                                  const IPAddressOrRange *const *b)
+{
+    return IPAddressOrRange_cmp(*a, *b, 16);
+}
+
+/*
+ * Calculate whether a range collapses to a prefix.
+ * See last paragraph of RFC 3779 2.2.3.7.
+ */
+static int range_should_be_prefix(const unsigned char *min,
+                                  const unsigned char *max, const int length)
+{
+    unsigned char mask;
+    int i, j;
+
+    if (memcmp(min, max, length) <= 0)
+        return -1;
+    for (i = 0; i < length && min[i] == max[i]; i++) ;
+    for (j = length - 1; j >= 0 && min[j] == 0x00 && max[j] == 0xFF; j--) ;
+    if (i < j)
+        return -1;
+    if (i > j)
+        return i * 8;
+    mask = min[i] ^ max[i];
+    switch (mask) {
+    case 0x01:
+        j = 7;
+        break;
+    case 0x03:
+        j = 6;
+        break;
+    case 0x07:
+        j = 5;
+        break;
+    case 0x0F:
+        j = 4;
+        break;
+    case 0x1F:
+        j = 3;
+        break;
+    case 0x3F:
+        j = 2;
+        break;
+    case 0x7F:
+        j = 1;
+        break;
+    default:
+        return -1;
+    }
+    if ((min[i] & mask) != 0 || (max[i] & mask) != mask)
+        return -1;
+    else
+        return i * 8 + j;
+}
+
+/*
+ * Construct a prefix.
+ */
+static int make_addressPrefix(IPAddressOrRange **result,
+                              unsigned char *addr, const int prefixlen)
+{
+    int bytelen = (prefixlen + 7) / 8, bitlen = prefixlen % 8;
+    IPAddressOrRange *aor = IPAddressOrRange_new();
+
+    if (aor == NULL)
+        return 0;
+    aor->type = IPAddressOrRange_addressPrefix;
+    if (aor->u.addressPrefix == NULL &&
+        (aor->u.addressPrefix = ASN1_BIT_STRING_new()) == NULL)
+        goto err;
+    if (!ASN1_BIT_STRING_set(aor->u.addressPrefix, addr, bytelen))
+        goto err;
+    aor->u.addressPrefix->flags &= ~7;
+    aor->u.addressPrefix->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+    if (bitlen > 0) {
+        aor->u.addressPrefix->data[bytelen - 1] &= ~(0xFF >> bitlen);
+        aor->u.addressPrefix->flags |= 8 - bitlen;
+    }
+
+    *result = aor;
+    return 1;
+
+ err:
+    IPAddressOrRange_free(aor);
+    return 0;
+}
+
+/*
+ * Construct a range.  If it can be expressed as a prefix,
+ * return a prefix instead.  Doing this here simplifies
+ * the rest of the code considerably.
+ */
+static int make_addressRange(IPAddressOrRange **result,
+                             unsigned char *min,
+                             unsigned char *max, const int length)
+{
+    IPAddressOrRange *aor;
+    int i, prefixlen;
+
+    if ((prefixlen = range_should_be_prefix(min, max, length)) >= 0)
+        return make_addressPrefix(result, min, prefixlen);
+
+    if ((aor = IPAddressOrRange_new()) == NULL)
+        return 0;
+    aor->type = IPAddressOrRange_addressRange;
+    if ((aor->u.addressRange = IPAddressRange_new()) == NULL)
+        goto err;
+    if (aor->u.addressRange->min == NULL &&
+        (aor->u.addressRange->min = ASN1_BIT_STRING_new()) == NULL)
+        goto err;
+    if (aor->u.addressRange->max == NULL &&
+        (aor->u.addressRange->max = ASN1_BIT_STRING_new()) == NULL)
+        goto err;
+
+    for (i = length; i > 0 && min[i - 1] == 0x00; --i) ;
+    if (!ASN1_BIT_STRING_set(aor->u.addressRange->min, min, i))
+        goto err;
+    aor->u.addressRange->min->flags &= ~7;
+    aor->u.addressRange->min->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+    if (i > 0) {
+        unsigned char b = min[i - 1];
+        int j = 1;
+        while ((b & (0xFFU >> j)) != 0)
+            ++j;
+        aor->u.addressRange->min->flags |= 8 - j;
+    }
+
+    for (i = length; i > 0 && max[i - 1] == 0xFF; --i) ;
+    if (!ASN1_BIT_STRING_set(aor->u.addressRange->max, max, i))
+        goto err;
+    aor->u.addressRange->max->flags &= ~7;
+    aor->u.addressRange->max->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+    if (i > 0) {
+        unsigned char b = max[i - 1];
+        int j = 1;
+        while ((b & (0xFFU >> j)) != (0xFFU >> j))
+            ++j;
+        aor->u.addressRange->max->flags |= 8 - j;
+    }
+
+    *result = aor;
+    return 1;
+
+ err:
+    IPAddressOrRange_free(aor);
+    return 0;
+}
+
+/*
+ * Construct a new address family or find an existing one.
+ */
+static IPAddressFamily *make_IPAddressFamily(IPAddrBlocks *addr,
+                                             const unsigned afi,
+                                             const unsigned *safi)
+{
+    IPAddressFamily *f;
+    unsigned char key[3];
+    int keylen;
+    int i;
+
+    key[0] = (afi >> 8) & 0xFF;
+    key[1] = afi & 0xFF;
+    if (safi != NULL) {
+        key[2] = *safi & 0xFF;
+        keylen = 3;
+    } else {
+        keylen = 2;
+    }
+
+    for (i = 0; i < sk_IPAddressFamily_num(addr); i++) {
+        f = sk_IPAddressFamily_value(addr, i);
+        if (f->addressFamily->length == keylen &&
+            !memcmp(f->addressFamily->data, key, keylen))
+            return f;
+    }
+
+    if ((f = IPAddressFamily_new()) == NULL)
+        goto err;
+    if (f->ipAddressChoice == NULL &&
+        (f->ipAddressChoice = IPAddressChoice_new()) == NULL)
+        goto err;
+    if (f->addressFamily == NULL &&
+        (f->addressFamily = ASN1_OCTET_STRING_new()) == NULL)
+        goto err;
+    if (!ASN1_OCTET_STRING_set(f->addressFamily, key, keylen))
+        goto err;
+    if (!sk_IPAddressFamily_push(addr, f))
+        goto err;
+
+    return f;
+
+ err:
+    IPAddressFamily_free(f);
+    return NULL;
+}
+
+/*
+ * Add an inheritance element.
+ */
+int X509v3_addr_add_inherit(IPAddrBlocks *addr,
+                            const unsigned afi, const unsigned *safi)
+{
+    IPAddressFamily *f = make_IPAddressFamily(addr, afi, safi);
+    if (f == NULL ||
+        f->ipAddressChoice == NULL ||
+        (f->ipAddressChoice->type == IPAddressChoice_addressesOrRanges &&
+         f->ipAddressChoice->u.addressesOrRanges != NULL))
+        return 0;
+    if (f->ipAddressChoice->type == IPAddressChoice_inherit &&
+        f->ipAddressChoice->u.inherit != NULL)
+        return 1;
+    if (f->ipAddressChoice->u.inherit == NULL &&
+        (f->ipAddressChoice->u.inherit = ASN1_NULL_new()) == NULL)
+        return 0;
+    f->ipAddressChoice->type = IPAddressChoice_inherit;
+    return 1;
+}
+
+/*
+ * Construct an IPAddressOrRange sequence, or return an existing one.
+ */
+static IPAddressOrRanges *make_prefix_or_range(IPAddrBlocks *addr,
+                                               const unsigned afi,
+                                               const unsigned *safi)
+{
+    IPAddressFamily *f = make_IPAddressFamily(addr, afi, safi);
+    IPAddressOrRanges *aors = NULL;
+
+    if (f == NULL ||
+        f->ipAddressChoice == NULL ||
+        (f->ipAddressChoice->type == IPAddressChoice_inherit &&
+         f->ipAddressChoice->u.inherit != NULL))
+        return NULL;
+    if (f->ipAddressChoice->type == IPAddressChoice_addressesOrRanges)
+        aors = f->ipAddressChoice->u.addressesOrRanges;
+    if (aors != NULL)
+        return aors;
+    if ((aors = sk_IPAddressOrRange_new_null()) == NULL)
+        return NULL;
+    switch (afi) {
+    case IANA_AFI_IPV4:
+        (void)sk_IPAddressOrRange_set_cmp_func(aors, v4IPAddressOrRange_cmp);
+        break;
+    case IANA_AFI_IPV6:
+        (void)sk_IPAddressOrRange_set_cmp_func(aors, v6IPAddressOrRange_cmp);
+        break;
+    }
+    f->ipAddressChoice->type = IPAddressChoice_addressesOrRanges;
+    f->ipAddressChoice->u.addressesOrRanges = aors;
+    return aors;
+}
+
+/*
+ * Add a prefix.
+ */
+int X509v3_addr_add_prefix(IPAddrBlocks *addr,
+                           const unsigned afi,
+                           const unsigned *safi,
+                           unsigned char *a, const int prefixlen)
+{
+    IPAddressOrRanges *aors = make_prefix_or_range(addr, afi, safi);
+    IPAddressOrRange *aor;
+    if (aors == NULL || !make_addressPrefix(&aor, a, prefixlen))
+        return 0;
+    if (sk_IPAddressOrRange_push(aors, aor))
+        return 1;
+    IPAddressOrRange_free(aor);
+    return 0;
+}
+
+/*
+ * Add a range.
+ */
+int X509v3_addr_add_range(IPAddrBlocks *addr,
+                          const unsigned afi,
+                          const unsigned *safi,
+                          unsigned char *min, unsigned char *max)
+{
+    IPAddressOrRanges *aors = make_prefix_or_range(addr, afi, safi);
+    IPAddressOrRange *aor;
+    int length = length_from_afi(afi);
+    if (aors == NULL)
+        return 0;
+    if (!make_addressRange(&aor, min, max, length))
+        return 0;
+    if (sk_IPAddressOrRange_push(aors, aor))
+        return 1;
+    IPAddressOrRange_free(aor);
+    return 0;
+}
+
+/*
+ * Extract min and max values from an IPAddressOrRange.
+ */
+static int extract_min_max(IPAddressOrRange *aor,
+                           unsigned char *min, unsigned char *max, int length)
+{
+    if (aor == NULL || min == NULL || max == NULL)
+        return 0;
+    switch (aor->type) {
+    case IPAddressOrRange_addressPrefix:
+        return (addr_expand(min, aor->u.addressPrefix, length, 0x00) &&
+                addr_expand(max, aor->u.addressPrefix, length, 0xFF));
+    case IPAddressOrRange_addressRange:
+        return (addr_expand(min, aor->u.addressRange->min, length, 0x00) &&
+                addr_expand(max, aor->u.addressRange->max, length, 0xFF));
+    }
+    return 0;
+}
+
+/*
+ * Public wrapper for extract_min_max().
+ */
+int X509v3_addr_get_range(IPAddressOrRange *aor,
+                          const unsigned afi,
+                          unsigned char *min,
+                          unsigned char *max, const int length)
+{
+    int afi_length = length_from_afi(afi);
+    if (aor == NULL || min == NULL || max == NULL ||
+        afi_length == 0 || length < afi_length ||
+        (aor->type != IPAddressOrRange_addressPrefix &&
+         aor->type != IPAddressOrRange_addressRange) ||
+        !extract_min_max(aor, min, max, afi_length))
+        return 0;
+
+    return afi_length;
+}
+
+/*
+ * Sort comparison function for a sequence of IPAddressFamily.
+ *
+ * The last paragraph of RFC 3779 2.2.3.3 is slightly ambiguous about
+ * the ordering: I can read it as meaning that IPv6 without a SAFI
+ * comes before IPv4 with a SAFI, which seems pretty weird.  The
+ * examples in appendix B suggest that the author intended the
+ * null-SAFI rule to apply only within a single AFI, which is what I
+ * would have expected and is what the following code implements.
+ */
+static int IPAddressFamily_cmp(const IPAddressFamily *const *a_,
+                               const IPAddressFamily *const *b_)
+{
+    const ASN1_OCTET_STRING *a = (*a_)->addressFamily;
+    const ASN1_OCTET_STRING *b = (*b_)->addressFamily;
+    int len = ((a->length <= b->length) ? a->length : b->length);
+    int cmp = memcmp(a->data, b->data, len);
+    return cmp ? cmp : a->length - b->length;
+}
+
+/*
+ * Check whether an IPAddrBLocks is in canonical form.
+ */
+int X509v3_addr_is_canonical(IPAddrBlocks *addr)
+{
+    unsigned char a_min[ADDR_RAW_BUF_LEN], a_max[ADDR_RAW_BUF_LEN];
+    unsigned char b_min[ADDR_RAW_BUF_LEN], b_max[ADDR_RAW_BUF_LEN];
+    IPAddressOrRanges *aors;
+    int i, j, k;
+
+    /*
+     * Empty extension is canonical.
+     */
+    if (addr == NULL)
+        return 1;
+
+    /*
+     * Check whether the top-level list is in order.
+     */
+    for (i = 0; i < sk_IPAddressFamily_num(addr) - 1; i++) {
+        const IPAddressFamily *a = sk_IPAddressFamily_value(addr, i);
+        const IPAddressFamily *b = sk_IPAddressFamily_value(addr, i + 1);
+        if (IPAddressFamily_cmp(&a, &b) >= 0)
+            return 0;
+    }
+
+    /*
+     * Top level's ok, now check each address family.
+     */
+    for (i = 0; i < sk_IPAddressFamily_num(addr); i++) {
+        IPAddressFamily *f = sk_IPAddressFamily_value(addr, i);
+        int length = length_from_afi(X509v3_addr_get_afi(f));
+
+        /*
+         * Inheritance is canonical.  Anything other than inheritance or
+         * a SEQUENCE OF IPAddressOrRange is an ASN.1 error or something.
+         */
+        if (f == NULL || f->ipAddressChoice == NULL)
+            return 0;
+        switch (f->ipAddressChoice->type) {
+        case IPAddressChoice_inherit:
+            continue;
+        case IPAddressChoice_addressesOrRanges:
+            break;
+        default:
+            return 0;
+        }
+
+        /*
+         * It's an IPAddressOrRanges sequence, check it.
+         */
+        aors = f->ipAddressChoice->u.addressesOrRanges;
+        if (sk_IPAddressOrRange_num(aors) == 0)
+            return 0;
+        for (j = 0; j < sk_IPAddressOrRange_num(aors) - 1; j++) {
+            IPAddressOrRange *a = sk_IPAddressOrRange_value(aors, j);
+            IPAddressOrRange *b = sk_IPAddressOrRange_value(aors, j + 1);
+
+            if (!extract_min_max(a, a_min, a_max, length) ||
+                !extract_min_max(b, b_min, b_max, length))
+                return 0;
+
+            /*
+             * Punt misordered list, overlapping start, or inverted range.
+             */
+            if (memcmp(a_min, b_min, length) >= 0 ||
+                memcmp(a_min, a_max, length) > 0 ||
+                memcmp(b_min, b_max, length) > 0)
+                return 0;
+
+            /*
+             * Punt if adjacent or overlapping.  Check for adjacency by
+             * subtracting one from b_min first.
+             */
+            for (k = length - 1; k >= 0 && b_min[k]-- == 0x00; k--) ;
+            if (memcmp(a_max, b_min, length) >= 0)
+                return 0;
+
+            /*
+             * Check for range that should be expressed as a prefix.
+             */
+            if (a->type == IPAddressOrRange_addressRange &&
+                range_should_be_prefix(a_min, a_max, length) >= 0)
+                return 0;
+        }
+
+        /*
+         * Check range to see if it's inverted or should be a
+         * prefix.
+         */
+        j = sk_IPAddressOrRange_num(aors) - 1;
+        {
+            IPAddressOrRange *a = sk_IPAddressOrRange_value(aors, j);
+            if (a != NULL && a->type == IPAddressOrRange_addressRange) {
+                if (!extract_min_max(a, a_min, a_max, length))
+                    return 0;
+                if (memcmp(a_min, a_max, length) > 0 ||
+                    range_should_be_prefix(a_min, a_max, length) >= 0)
+                    return 0;
+            }
+        }
+    }
+
+    /*
+     * If we made it through all that, we're happy.
+     */
+    return 1;
+}
+
+/*
+ * Whack an IPAddressOrRanges into canonical form.
+ */
+static int IPAddressOrRanges_canonize(IPAddressOrRanges *aors,
+                                      const unsigned afi)
+{
+    int i, j, length = length_from_afi(afi);
+
+    /*
+     * Sort the IPAddressOrRanges sequence.
+     */
+    sk_IPAddressOrRange_sort(aors);
+
+    /*
+     * Clean up representation issues, punt on duplicates or overlaps.
+     */
+    for (i = 0; i < sk_IPAddressOrRange_num(aors) - 1; i++) {
+        IPAddressOrRange *a = sk_IPAddressOrRange_value(aors, i);
+        IPAddressOrRange *b = sk_IPAddressOrRange_value(aors, i + 1);
+        unsigned char a_min[ADDR_RAW_BUF_LEN], a_max[ADDR_RAW_BUF_LEN];
+        unsigned char b_min[ADDR_RAW_BUF_LEN], b_max[ADDR_RAW_BUF_LEN];
+
+        if (!extract_min_max(a, a_min, a_max, length) ||
+            !extract_min_max(b, b_min, b_max, length))
+            return 0;
+
+        /*
+         * Punt inverted ranges.
+         */
+        if (memcmp(a_min, a_max, length) > 0 ||
+            memcmp(b_min, b_max, length) > 0)
+            return 0;
+
+        /*
+         * Punt overlaps.
+         */
+        if (memcmp(a_max, b_min, length) >= 0)
+            return 0;
+
+        /*
+         * Merge if a and b are adjacent.  We check for
+         * adjacency by subtracting one from b_min first.
+         */
+        for (j = length - 1; j >= 0 && b_min[j]-- == 0x00; j--) ;
+        if (memcmp(a_max, b_min, length) == 0) {
+            IPAddressOrRange *merged;
+            if (!make_addressRange(&merged, a_min, b_max, length))
+                return 0;
+            (void)sk_IPAddressOrRange_set(aors, i, merged);
+            (void)sk_IPAddressOrRange_delete(aors, i + 1);
+            IPAddressOrRange_free(a);
+            IPAddressOrRange_free(b);
+            --i;
+            continue;
+        }
+    }
+
+    /*
+     * Check for inverted final range.
+     */
+    j = sk_IPAddressOrRange_num(aors) - 1;
+    {
+        IPAddressOrRange *a = sk_IPAddressOrRange_value(aors, j);
+        if (a != NULL && a->type == IPAddressOrRange_addressRange) {
+            unsigned char a_min[ADDR_RAW_BUF_LEN], a_max[ADDR_RAW_BUF_LEN];
+            if (!extract_min_max(a, a_min, a_max, length))
+                return 0;
+            if (memcmp(a_min, a_max, length) > 0)
+                return 0;
+        }
+    }
+
+    return 1;
+}
+
+/*
+ * Whack an IPAddrBlocks extension into canonical form.
+ */
+int X509v3_addr_canonize(IPAddrBlocks *addr)
+{
+    int i;
+    for (i = 0; i < sk_IPAddressFamily_num(addr); i++) {
+        IPAddressFamily *f = sk_IPAddressFamily_value(addr, i);
+        if (f->ipAddressChoice->type == IPAddressChoice_addressesOrRanges &&
+            !IPAddressOrRanges_canonize(f->ipAddressChoice->
+                                        u.addressesOrRanges,
+                                        X509v3_addr_get_afi(f)))
+            return 0;
+    }
+    (void)sk_IPAddressFamily_set_cmp_func(addr, IPAddressFamily_cmp);
+    sk_IPAddressFamily_sort(addr);
+    if (!ossl_assert(X509v3_addr_is_canonical(addr)))
+        return 0;
+    return 1;
+}
+
+/*
+ * v2i handler for the IPAddrBlocks extension.
+ */
+static void *v2i_IPAddrBlocks(const struct v3_ext_method *method,
+                              struct v3_ext_ctx *ctx,
+                              STACK_OF(CONF_VALUE) *values)
+{
+    static const char v4addr_chars[] = "0123456789.";
+    static const char v6addr_chars[] = "0123456789.:abcdefABCDEF";
+    IPAddrBlocks *addr = NULL;
+    char *s = NULL, *t;
+    int i;
+
+    if ((addr = sk_IPAddressFamily_new(IPAddressFamily_cmp)) == NULL) {
+        X509V3err(X509V3_F_V2I_IPADDRBLOCKS, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    for (i = 0; i < sk_CONF_VALUE_num(values); i++) {
+        CONF_VALUE *val = sk_CONF_VALUE_value(values, i);
+        unsigned char min[ADDR_RAW_BUF_LEN], max[ADDR_RAW_BUF_LEN];
+        unsigned afi, *safi = NULL, safi_;
+        const char *addr_chars = NULL;
+        int prefixlen, i1, i2, delim, length;
+
+        if (!name_cmp(val->name, "IPv4")) {
+            afi = IANA_AFI_IPV4;
+        } else if (!name_cmp(val->name, "IPv6")) {
+            afi = IANA_AFI_IPV6;
+        } else if (!name_cmp(val->name, "IPv4-SAFI")) {
+            afi = IANA_AFI_IPV4;
+            safi = &safi_;
+        } else if (!name_cmp(val->name, "IPv6-SAFI")) {
+            afi = IANA_AFI_IPV6;
+            safi = &safi_;
+        } else {
+            X509V3err(X509V3_F_V2I_IPADDRBLOCKS,
+                      X509V3_R_EXTENSION_NAME_ERROR);
+            X509V3_conf_err(val);
+            goto err;
+        }
+
+        switch (afi) {
+        case IANA_AFI_IPV4:
+            addr_chars = v4addr_chars;
+            break;
+        case IANA_AFI_IPV6:
+            addr_chars = v6addr_chars;
+            break;
+        }
+
+        length = length_from_afi(afi);
+
+        /*
+         * Handle SAFI, if any, and OPENSSL_strdup() so we can null-terminate
+         * the other input values.
+         */
+        if (safi != NULL) {
+            *safi = strtoul(val->value, &t, 0);
+            t += strspn(t, " \t");
+            if (*safi > 0xFF || *t++ != ':') {
+                X509V3err(X509V3_F_V2I_IPADDRBLOCKS, X509V3_R_INVALID_SAFI);
+                X509V3_conf_err(val);
+                goto err;
+            }
+            t += strspn(t, " \t");
+            s = OPENSSL_strdup(t);
+        } else {
+            s = OPENSSL_strdup(val->value);
+        }
+        if (s == NULL) {
+            X509V3err(X509V3_F_V2I_IPADDRBLOCKS, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+
+        /*
+         * Check for inheritance.  Not worth additional complexity to
+         * optimize this (seldom-used) case.
+         */
+        if (strcmp(s, "inherit") == 0) {
+            if (!X509v3_addr_add_inherit(addr, afi, safi)) {
+                X509V3err(X509V3_F_V2I_IPADDRBLOCKS,
+                          X509V3_R_INVALID_INHERITANCE);
+                X509V3_conf_err(val);
+                goto err;
+            }
+            OPENSSL_free(s);
+            s = NULL;
+            continue;
+        }
+
+        i1 = strspn(s, addr_chars);
+        i2 = i1 + strspn(s + i1, " \t");
+        delim = s[i2++];
+        s[i1] = '\0';
+
+        if (a2i_ipadd(min, s) != length) {
+            X509V3err(X509V3_F_V2I_IPADDRBLOCKS, X509V3_R_INVALID_IPADDRESS);
+            X509V3_conf_err(val);
+            goto err;
+        }
+
+        switch (delim) {
+        case '/':
+            prefixlen = (int)strtoul(s + i2, &t, 10);
+            if (t == s + i2 || *t != '\0') {
+                X509V3err(X509V3_F_V2I_IPADDRBLOCKS,
+                          X509V3_R_EXTENSION_VALUE_ERROR);
+                X509V3_conf_err(val);
+                goto err;
+            }
+            if (!X509v3_addr_add_prefix(addr, afi, safi, min, prefixlen)) {
+                X509V3err(X509V3_F_V2I_IPADDRBLOCKS, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+            break;
+        case '-':
+            i1 = i2 + strspn(s + i2, " \t");
+            i2 = i1 + strspn(s + i1, addr_chars);
+            if (i1 == i2 || s[i2] != '\0') {
+                X509V3err(X509V3_F_V2I_IPADDRBLOCKS,
+                          X509V3_R_EXTENSION_VALUE_ERROR);
+                X509V3_conf_err(val);
+                goto err;
+            }
+            if (a2i_ipadd(max, s + i1) != length) {
+                X509V3err(X509V3_F_V2I_IPADDRBLOCKS,
+                          X509V3_R_INVALID_IPADDRESS);
+                X509V3_conf_err(val);
+                goto err;
+            }
+            if (memcmp(min, max, length_from_afi(afi)) > 0) {
+                X509V3err(X509V3_F_V2I_IPADDRBLOCKS,
+                          X509V3_R_EXTENSION_VALUE_ERROR);
+                X509V3_conf_err(val);
+                goto err;
+            }
+            if (!X509v3_addr_add_range(addr, afi, safi, min, max)) {
+                X509V3err(X509V3_F_V2I_IPADDRBLOCKS, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+            break;
+        case '\0':
+            if (!X509v3_addr_add_prefix(addr, afi, safi, min, length * 8)) {
+                X509V3err(X509V3_F_V2I_IPADDRBLOCKS, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+            break;
+        default:
+            X509V3err(X509V3_F_V2I_IPADDRBLOCKS,
+                      X509V3_R_EXTENSION_VALUE_ERROR);
+            X509V3_conf_err(val);
+            goto err;
+        }
+
+        OPENSSL_free(s);
+        s = NULL;
+    }
+
+    /*
+     * Canonize the result, then we're done.
+     */
+    if (!X509v3_addr_canonize(addr))
+        goto err;
+    return addr;
+
+ err:
+    OPENSSL_free(s);
+    sk_IPAddressFamily_pop_free(addr, IPAddressFamily_free);
+    return NULL;
+}
+
+/*
+ * OpenSSL dispatch
+ */
+const X509V3_EXT_METHOD v3_addr = {
+    NID_sbgp_ipAddrBlock,       /* nid */
+    0,                          /* flags */
+    ASN1_ITEM_ref(IPAddrBlocks), /* template */
+    0, 0, 0, 0,                 /* old functions, ignored */
+    0,                          /* i2s */
+    0,                          /* s2i */
+    0,                          /* i2v */
+    v2i_IPAddrBlocks,           /* v2i */
+    i2r_IPAddrBlocks,           /* i2r */
+    0,                          /* r2i */
+    NULL                        /* extension-specific data */
+};
+
+/*
+ * Figure out whether extension sues inheritance.
+ */
+int X509v3_addr_inherits(IPAddrBlocks *addr)
+{
+    int i;
+    if (addr == NULL)
+        return 0;
+    for (i = 0; i < sk_IPAddressFamily_num(addr); i++) {
+        IPAddressFamily *f = sk_IPAddressFamily_value(addr, i);
+        if (f->ipAddressChoice->type == IPAddressChoice_inherit)
+            return 1;
+    }
+    return 0;
+}
+
+/*
+ * Figure out whether parent contains child.
+ */
+static int addr_contains(IPAddressOrRanges *parent,
+                         IPAddressOrRanges *child, int length)
+{
+    unsigned char p_min[ADDR_RAW_BUF_LEN], p_max[ADDR_RAW_BUF_LEN];
+    unsigned char c_min[ADDR_RAW_BUF_LEN], c_max[ADDR_RAW_BUF_LEN];
+    int p, c;
+
+    if (child == NULL || parent == child)
+        return 1;
+    if (parent == NULL)
+        return 0;
+
+    p = 0;
+    for (c = 0; c < sk_IPAddressOrRange_num(child); c++) {
+        if (!extract_min_max(sk_IPAddressOrRange_value(child, c),
+                             c_min, c_max, length))
+            return -1;
+        for (;; p++) {
+            if (p >= sk_IPAddressOrRange_num(parent))
+                return 0;
+            if (!extract_min_max(sk_IPAddressOrRange_value(parent, p),
+                                 p_min, p_max, length))
+                return 0;
+            if (memcmp(p_max, c_max, length) < 0)
+                continue;
+            if (memcmp(p_min, c_min, length) > 0)
+                return 0;
+            break;
+        }
+    }
+
+    return 1;
+}
+
+/*
+ * Test whether a is a subset of b.
+ */
+int X509v3_addr_subset(IPAddrBlocks *a, IPAddrBlocks *b)
+{
+    int i;
+    if (a == NULL || a == b)
+        return 1;
+    if (b == NULL || X509v3_addr_inherits(a) || X509v3_addr_inherits(b))
+        return 0;
+    (void)sk_IPAddressFamily_set_cmp_func(b, IPAddressFamily_cmp);
+    for (i = 0; i < sk_IPAddressFamily_num(a); i++) {
+        IPAddressFamily *fa = sk_IPAddressFamily_value(a, i);
+        int j = sk_IPAddressFamily_find(b, fa);
+        IPAddressFamily *fb;
+        fb = sk_IPAddressFamily_value(b, j);
+        if (fb == NULL)
+            return 0;
+        if (!addr_contains(fb->ipAddressChoice->u.addressesOrRanges,
+                           fa->ipAddressChoice->u.addressesOrRanges,
+                           length_from_afi(X509v3_addr_get_afi(fb))))
+            return 0;
+    }
+    return 1;
+}
+
+/*
+ * Validation error handling via callback.
+ */
+#define validation_err(_err_)           \
+  do {                                  \
+    if (ctx != NULL) {                  \
+      ctx->error = _err_;               \
+      ctx->error_depth = i;             \
+      ctx->current_cert = x;            \
+      ret = ctx->verify_cb(0, ctx);     \
+    } else {                            \
+      ret = 0;                          \
+    }                                   \
+    if (!ret)                           \
+      goto done;                        \
+  } while (0)
+
+/*
+ * Core code for RFC 3779 2.3 path validation.
+ *
+ * Returns 1 for success, 0 on error.
+ *
+ * When returning 0, ctx->error MUST be set to an appropriate value other than
+ * X509_V_OK.
+ */
+static int addr_validate_path_internal(X509_STORE_CTX *ctx,
+                                       STACK_OF(X509) *chain,
+                                       IPAddrBlocks *ext)
+{
+    IPAddrBlocks *child = NULL;
+    int i, j, ret = 1;
+    X509 *x;
+
+    if (!ossl_assert(chain != NULL && sk_X509_num(chain) > 0)
+            || !ossl_assert(ctx != NULL || ext != NULL)
+            || !ossl_assert(ctx == NULL || ctx->verify_cb != NULL)) {
+        if (ctx != NULL)
+            ctx->error = X509_V_ERR_UNSPECIFIED;
+        return 0;
+    }
+
+    /*
+     * Figure out where to start.  If we don't have an extension to
+     * check, we're done.  Otherwise, check canonical form and
+     * set up for walking up the chain.
+     */
+    if (ext != NULL) {
+        i = -1;
+        x = NULL;
+    } else {
+        i = 0;
+        x = sk_X509_value(chain, i);
+        if ((ext = x->rfc3779_addr) == NULL)
+            goto done;
+    }
+    if (!X509v3_addr_is_canonical(ext))
+        validation_err(X509_V_ERR_INVALID_EXTENSION);
+    (void)sk_IPAddressFamily_set_cmp_func(ext, IPAddressFamily_cmp);
+    if ((child = sk_IPAddressFamily_dup(ext)) == NULL) {
+        X509V3err(X509V3_F_ADDR_VALIDATE_PATH_INTERNAL,
+                  ERR_R_MALLOC_FAILURE);
+        if (ctx != NULL)
+            ctx->error = X509_V_ERR_OUT_OF_MEM;
+        ret = 0;
+        goto done;
+    }
+
+    /*
+     * Now walk up the chain.  No cert may list resources that its
+     * parent doesn't list.
+     */
+    for (i++; i < sk_X509_num(chain); i++) {
+        x = sk_X509_value(chain, i);
+        if (!X509v3_addr_is_canonical(x->rfc3779_addr))
+            validation_err(X509_V_ERR_INVALID_EXTENSION);
+        if (x->rfc3779_addr == NULL) {
+            for (j = 0; j < sk_IPAddressFamily_num(child); j++) {
+                IPAddressFamily *fc = sk_IPAddressFamily_value(child, j);
+                if (fc->ipAddressChoice->type != IPAddressChoice_inherit) {
+                    validation_err(X509_V_ERR_UNNESTED_RESOURCE);
+                    break;
+                }
+            }
+            continue;
+        }
+        (void)sk_IPAddressFamily_set_cmp_func(x->rfc3779_addr,
+                                              IPAddressFamily_cmp);
+        for (j = 0; j < sk_IPAddressFamily_num(child); j++) {
+            IPAddressFamily *fc = sk_IPAddressFamily_value(child, j);
+            int k = sk_IPAddressFamily_find(x->rfc3779_addr, fc);
+            IPAddressFamily *fp =
+                sk_IPAddressFamily_value(x->rfc3779_addr, k);
+            if (fp == NULL) {
+                if (fc->ipAddressChoice->type ==
+                    IPAddressChoice_addressesOrRanges) {
+                    validation_err(X509_V_ERR_UNNESTED_RESOURCE);
+                    break;
+                }
+                continue;
+            }
+            if (fp->ipAddressChoice->type ==
+                IPAddressChoice_addressesOrRanges) {
+                if (fc->ipAddressChoice->type == IPAddressChoice_inherit
+                    || addr_contains(fp->ipAddressChoice->u.addressesOrRanges,
+                                     fc->ipAddressChoice->u.addressesOrRanges,
+                                     length_from_afi(X509v3_addr_get_afi(fc))))
+                    sk_IPAddressFamily_set(child, j, fp);
+                else
+                    validation_err(X509_V_ERR_UNNESTED_RESOURCE);
+            }
+        }
+    }
+
+    /*
+     * Trust anchor can't inherit.
+     */
+    if (x->rfc3779_addr != NULL) {
+        for (j = 0; j < sk_IPAddressFamily_num(x->rfc3779_addr); j++) {
+            IPAddressFamily *fp =
+                sk_IPAddressFamily_value(x->rfc3779_addr, j);
+            if (fp->ipAddressChoice->type == IPAddressChoice_inherit
+                && sk_IPAddressFamily_find(child, fp) >= 0)
+                validation_err(X509_V_ERR_UNNESTED_RESOURCE);
+        }
+    }
+
+ done:
+    sk_IPAddressFamily_free(child);
+    return ret;
+}
+
+#undef validation_err
+
+/*
+ * RFC 3779 2.3 path validation -- called from X509_verify_cert().
+ */
+int X509v3_addr_validate_path(X509_STORE_CTX *ctx)
+{
+    if (ctx->chain == NULL
+            || sk_X509_num(ctx->chain) == 0
+            || ctx->verify_cb == NULL) {
+        ctx->error = X509_V_ERR_UNSPECIFIED;
+        return 0;
+    }
+    return addr_validate_path_internal(ctx, ctx->chain, NULL);
+}
+
+/*
+ * RFC 3779 2.3 path validation of an extension.
+ * Test whether chain covers extension.
+ */
+int X509v3_addr_validate_resource_set(STACK_OF(X509) *chain,
+                                  IPAddrBlocks *ext, int allow_inheritance)
+{
+    if (ext == NULL)
+        return 1;
+    if (chain == NULL || sk_X509_num(chain) == 0)
+        return 0;
+    if (!allow_inheritance && X509v3_addr_inherits(ext))
+        return 0;
+    return addr_validate_path_internal(NULL, chain, ext);
+}
+
+#endif                          /* OPENSSL_NO_RFC3779 */
diff --git a/lib/libcrypto/x509/x509_asid.c b/lib/libcrypto/x509/x509_asid.c
new file mode 100644 (file)
index 0000000..ac68572
--- /dev/null
@@ -0,0 +1,894 @@
+/*
+ * Copyright 2006-2018 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Implementation of RFC 3779 section 3.2.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include "internal/cryptlib.h"
+#include <openssl/conf.h>
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/x509v3.h>
+#include <openssl/x509.h>
+#include "crypto/x509.h"
+#include <openssl/bn.h>
+#include "ext_dat.h"
+
+#ifndef OPENSSL_NO_RFC3779
+
+/*
+ * OpenSSL ASN.1 template translation of RFC 3779 3.2.3.
+ */
+
+ASN1_SEQUENCE(ASRange) = {
+  ASN1_SIMPLE(ASRange, min, ASN1_INTEGER),
+  ASN1_SIMPLE(ASRange, max, ASN1_INTEGER)
+} ASN1_SEQUENCE_END(ASRange)
+
+ASN1_CHOICE(ASIdOrRange) = {
+  ASN1_SIMPLE(ASIdOrRange, u.id,    ASN1_INTEGER),
+  ASN1_SIMPLE(ASIdOrRange, u.range, ASRange)
+} ASN1_CHOICE_END(ASIdOrRange)
+
+ASN1_CHOICE(ASIdentifierChoice) = {
+  ASN1_SIMPLE(ASIdentifierChoice,      u.inherit,       ASN1_NULL),
+  ASN1_SEQUENCE_OF(ASIdentifierChoice, u.asIdsOrRanges, ASIdOrRange)
+} ASN1_CHOICE_END(ASIdentifierChoice)
+
+ASN1_SEQUENCE(ASIdentifiers) = {
+  ASN1_EXP_OPT(ASIdentifiers, asnum, ASIdentifierChoice, 0),
+  ASN1_EXP_OPT(ASIdentifiers, rdi,   ASIdentifierChoice, 1)
+} ASN1_SEQUENCE_END(ASIdentifiers)
+
+IMPLEMENT_ASN1_FUNCTIONS(ASRange)
+IMPLEMENT_ASN1_FUNCTIONS(ASIdOrRange)
+IMPLEMENT_ASN1_FUNCTIONS(ASIdentifierChoice)
+IMPLEMENT_ASN1_FUNCTIONS(ASIdentifiers)
+
+/*
+ * i2r method for an ASIdentifierChoice.
+ */
+static int i2r_ASIdentifierChoice(BIO *out,
+                                  ASIdentifierChoice *choice,
+                                  int indent, const char *msg)
+{
+    int i;
+    char *s;
+    if (choice == NULL)
+        return 1;
+    BIO_printf(out, "%*s%s:\n", indent, "", msg);
+    switch (choice->type) {
+    case ASIdentifierChoice_inherit:
+        BIO_printf(out, "%*sinherit\n", indent + 2, "");
+        break;
+    case ASIdentifierChoice_asIdsOrRanges:
+        for (i = 0; i < sk_ASIdOrRange_num(choice->u.asIdsOrRanges); i++) {
+            ASIdOrRange *aor =
+                sk_ASIdOrRange_value(choice->u.asIdsOrRanges, i);
+            switch (aor->type) {
+            case ASIdOrRange_id:
+                if ((s = i2s_ASN1_INTEGER(NULL, aor->u.id)) == NULL)
+                    return 0;
+                BIO_printf(out, "%*s%s\n", indent + 2, "", s);
+                OPENSSL_free(s);
+                break;
+            case ASIdOrRange_range:
+                if ((s = i2s_ASN1_INTEGER(NULL, aor->u.range->min)) == NULL)
+                    return 0;
+                BIO_printf(out, "%*s%s-", indent + 2, "", s);
+                OPENSSL_free(s);
+                if ((s = i2s_ASN1_INTEGER(NULL, aor->u.range->max)) == NULL)
+                    return 0;
+                BIO_printf(out, "%s\n", s);
+                OPENSSL_free(s);
+                break;
+            default:
+                return 0;
+            }
+        }
+        break;
+    default:
+        return 0;
+    }
+    return 1;
+}
+
+/*
+ * i2r method for an ASIdentifier extension.
+ */
+static int i2r_ASIdentifiers(const X509V3_EXT_METHOD *method,
+                             void *ext, BIO *out, int indent)
+{
+    ASIdentifiers *asid = ext;
+    return (i2r_ASIdentifierChoice(out, asid->asnum, indent,
+                                   "Autonomous System Numbers") &&
+            i2r_ASIdentifierChoice(out, asid->rdi, indent,
+                                   "Routing Domain Identifiers"));
+}
+
+/*
+ * Sort comparison function for a sequence of ASIdOrRange elements.
+ */
+static int ASIdOrRange_cmp(const ASIdOrRange *const *a_,
+                           const ASIdOrRange *const *b_)
+{
+    const ASIdOrRange *a = *a_, *b = *b_;
+
+    assert((a->type == ASIdOrRange_id && a->u.id != NULL) ||
+           (a->type == ASIdOrRange_range && a->u.range != NULL &&
+            a->u.range->min != NULL && a->u.range->max != NULL));
+
+    assert((b->type == ASIdOrRange_id && b->u.id != NULL) ||
+           (b->type == ASIdOrRange_range && b->u.range != NULL &&
+            b->u.range->min != NULL && b->u.range->max != NULL));
+
+    if (a->type == ASIdOrRange_id && b->type == ASIdOrRange_id)
+        return ASN1_INTEGER_cmp(a->u.id, b->u.id);
+
+    if (a->type == ASIdOrRange_range && b->type == ASIdOrRange_range) {
+        int r = ASN1_INTEGER_cmp(a->u.range->min, b->u.range->min);
+        return r != 0 ? r : ASN1_INTEGER_cmp(a->u.range->max,
+                                             b->u.range->max);
+    }
+
+    if (a->type == ASIdOrRange_id)
+        return ASN1_INTEGER_cmp(a->u.id, b->u.range->min);
+    else
+        return ASN1_INTEGER_cmp(a->u.range->min, b->u.id);
+}
+
+/*
+ * Add an inherit element.
+ */
+int X509v3_asid_add_inherit(ASIdentifiers *asid, int which)
+{
+    ASIdentifierChoice **choice;
+    if (asid == NULL)
+        return 0;
+    switch (which) {
+    case V3_ASID_ASNUM:
+        choice = &asid->asnum;
+        break;
+    case V3_ASID_RDI:
+        choice = &asid->rdi;
+        break;
+    default:
+        return 0;
+    }
+    if (*choice == NULL) {
+        if ((*choice = ASIdentifierChoice_new()) == NULL)
+            return 0;
+        if (((*choice)->u.inherit = ASN1_NULL_new()) == NULL)
+            return 0;
+        (*choice)->type = ASIdentifierChoice_inherit;
+    }
+    return (*choice)->type == ASIdentifierChoice_inherit;
+}
+
+/*
+ * Add an ID or range to an ASIdentifierChoice.
+ */
+int X509v3_asid_add_id_or_range(ASIdentifiers *asid,
+                                int which, ASN1_INTEGER *min, ASN1_INTEGER *max)
+{
+    ASIdentifierChoice **choice;
+    ASIdOrRange *aor;
+    if (asid == NULL)
+        return 0;
+    switch (which) {
+    case V3_ASID_ASNUM:
+        choice = &asid->asnum;
+        break;
+    case V3_ASID_RDI:
+        choice = &asid->rdi;
+        break;
+    default:
+        return 0;
+    }
+    if (*choice != NULL && (*choice)->type == ASIdentifierChoice_inherit)
+        return 0;
+    if (*choice == NULL) {
+        if ((*choice = ASIdentifierChoice_new()) == NULL)
+            return 0;
+        (*choice)->u.asIdsOrRanges = sk_ASIdOrRange_new(ASIdOrRange_cmp);
+        if ((*choice)->u.asIdsOrRanges == NULL)
+            return 0;
+        (*choice)->type = ASIdentifierChoice_asIdsOrRanges;
+    }
+    if ((aor = ASIdOrRange_new()) == NULL)
+        return 0;
+    if (max == NULL) {
+        aor->type = ASIdOrRange_id;
+        aor->u.id = min;
+    } else {
+        aor->type = ASIdOrRange_range;
+        if ((aor->u.range = ASRange_new()) == NULL)
+            goto err;
+        ASN1_INTEGER_free(aor->u.range->min);
+        aor->u.range->min = min;
+        ASN1_INTEGER_free(aor->u.range->max);
+        aor->u.range->max = max;
+    }
+    if (!(sk_ASIdOrRange_push((*choice)->u.asIdsOrRanges, aor)))
+        goto err;
+    return 1;
+
+ err:
+    ASIdOrRange_free(aor);
+    return 0;
+}
+
+/*
+ * Extract min and max values from an ASIdOrRange.
+ */
+static int extract_min_max(ASIdOrRange *aor,
+                           ASN1_INTEGER **min, ASN1_INTEGER **max)
+{
+    if (!ossl_assert(aor != NULL))
+        return 0;
+    switch (aor->type) {
+    case ASIdOrRange_id:
+        *min = aor->u.id;
+        *max = aor->u.id;
+        return 1;
+    case ASIdOrRange_range:
+        *min = aor->u.range->min;
+        *max = aor->u.range->max;
+        return 1;
+    }
+
+    return 0;
+}
+
+/*
+ * Check whether an ASIdentifierChoice is in canonical form.
+ */
+static int ASIdentifierChoice_is_canonical(ASIdentifierChoice *choice)
+{
+    ASN1_INTEGER *a_max_plus_one = NULL;
+    ASN1_INTEGER *orig;
+    BIGNUM *bn = NULL;
+    int i, ret = 0;
+
+    /*
+     * Empty element or inheritance is canonical.
+     */
+    if (choice == NULL || choice->type == ASIdentifierChoice_inherit)
+        return 1;
+
+    /*
+     * If not a list, or if empty list, it's broken.
+     */
+    if (choice->type != ASIdentifierChoice_asIdsOrRanges ||
+        sk_ASIdOrRange_num(choice->u.asIdsOrRanges) == 0)
+        return 0;
+
+    /*
+     * It's a list, check it.
+     */
+    for (i = 0; i < sk_ASIdOrRange_num(choice->u.asIdsOrRanges) - 1; i++) {
+        ASIdOrRange *a = sk_ASIdOrRange_value(choice->u.asIdsOrRanges, i);
+        ASIdOrRange *b = sk_ASIdOrRange_value(choice->u.asIdsOrRanges, i + 1);
+        ASN1_INTEGER *a_min = NULL, *a_max = NULL, *b_min = NULL, *b_max =
+            NULL;
+
+        if (!extract_min_max(a, &a_min, &a_max)
+                || !extract_min_max(b, &b_min, &b_max))
+            goto done;
+
+        /*
+         * Punt misordered list, overlapping start, or inverted range.
+         */
+        if (ASN1_INTEGER_cmp(a_min, b_min) >= 0 ||
+            ASN1_INTEGER_cmp(a_min, a_max) > 0 ||
+            ASN1_INTEGER_cmp(b_min, b_max) > 0)
+            goto done;
+
+        /*
+         * Calculate a_max + 1 to check for adjacency.
+         */
+        if ((bn == NULL && (bn = BN_new()) == NULL) ||
+            ASN1_INTEGER_to_BN(a_max, bn) == NULL ||
+            !BN_add_word(bn, 1)) {
+            X509V3err(X509V3_F_ASIDENTIFIERCHOICE_IS_CANONICAL,
+                      ERR_R_MALLOC_FAILURE);
+            goto done;
+        }
+
+        if ((a_max_plus_one =
+                BN_to_ASN1_INTEGER(bn, orig = a_max_plus_one)) == NULL) {
+            a_max_plus_one = orig;
+            X509V3err(X509V3_F_ASIDENTIFIERCHOICE_IS_CANONICAL,
+                      ERR_R_MALLOC_FAILURE);
+            goto done;
+        }
+
+        /*
+         * Punt if adjacent or overlapping.
+         */
+        if (ASN1_INTEGER_cmp(a_max_plus_one, b_min) >= 0)
+            goto done;
+    }
+
+    /*
+     * Check for inverted range.
+     */
+    i = sk_ASIdOrRange_num(choice->u.asIdsOrRanges) - 1;
+    {
+        ASIdOrRange *a = sk_ASIdOrRange_value(choice->u.asIdsOrRanges, i);
+        ASN1_INTEGER *a_min, *a_max;
+        if (a != NULL && a->type == ASIdOrRange_range) {
+            if (!extract_min_max(a, &a_min, &a_max)
+                    || ASN1_INTEGER_cmp(a_min, a_max) > 0)
+                goto done;
+        }
+    }
+
+    ret = 1;
+
+ done:
+    ASN1_INTEGER_free(a_max_plus_one);
+    BN_free(bn);
+    return ret;
+}
+
+/*
+ * Check whether an ASIdentifier extension is in canonical form.
+ */
+int X509v3_asid_is_canonical(ASIdentifiers *asid)
+{
+    return (asid == NULL ||
+            (ASIdentifierChoice_is_canonical(asid->asnum) &&
+             ASIdentifierChoice_is_canonical(asid->rdi)));
+}
+
+/*
+ * Whack an ASIdentifierChoice into canonical form.
+ */
+static int ASIdentifierChoice_canonize(ASIdentifierChoice *choice)
+{
+    ASN1_INTEGER *a_max_plus_one = NULL;
+    ASN1_INTEGER *orig;
+    BIGNUM *bn = NULL;
+    int i, ret = 0;
+
+    /*
+     * Nothing to do for empty element or inheritance.
+     */
+    if (choice == NULL || choice->type == ASIdentifierChoice_inherit)
+        return 1;
+
+    /*
+     * If not a list, or if empty list, it's broken.
+     */
+    if (choice->type != ASIdentifierChoice_asIdsOrRanges ||
+        sk_ASIdOrRange_num(choice->u.asIdsOrRanges) == 0) {
+        X509V3err(X509V3_F_ASIDENTIFIERCHOICE_CANONIZE,
+                  X509V3_R_EXTENSION_VALUE_ERROR);
+        return 0;
+    }
+
+    /*
+     * We have a non-empty list.  Sort it.
+     */
+    sk_ASIdOrRange_sort(choice->u.asIdsOrRanges);
+
+    /*
+     * Now check for errors and suboptimal encoding, rejecting the
+     * former and fixing the latter.
+     */
+    for (i = 0; i < sk_ASIdOrRange_num(choice->u.asIdsOrRanges) - 1; i++) {
+        ASIdOrRange *a = sk_ASIdOrRange_value(choice->u.asIdsOrRanges, i);
+        ASIdOrRange *b = sk_ASIdOrRange_value(choice->u.asIdsOrRanges, i + 1);
+        ASN1_INTEGER *a_min = NULL, *a_max = NULL, *b_min = NULL, *b_max =
+            NULL;
+
+        if (!extract_min_max(a, &a_min, &a_max)
+                || !extract_min_max(b, &b_min, &b_max))
+            goto done;
+
+        /*
+         * Make sure we're properly sorted (paranoia).
+         */
+        if (!ossl_assert(ASN1_INTEGER_cmp(a_min, b_min) <= 0))
+            goto done;
+
+        /*
+         * Punt inverted ranges.
+         */
+        if (ASN1_INTEGER_cmp(a_min, a_max) > 0 ||
+            ASN1_INTEGER_cmp(b_min, b_max) > 0)
+            goto done;
+
+        /*
+         * Check for overlaps.
+         */
+        if (ASN1_INTEGER_cmp(a_max, b_min) >= 0) {
+            X509V3err(X509V3_F_ASIDENTIFIERCHOICE_CANONIZE,
+                      X509V3_R_EXTENSION_VALUE_ERROR);
+            goto done;
+        }
+
+        /*
+         * Calculate a_max + 1 to check for adjacency.
+         */
+        if ((bn == NULL && (bn = BN_new()) == NULL) ||
+            ASN1_INTEGER_to_BN(a_max, bn) == NULL ||
+            !BN_add_word(bn, 1)) {
+            X509V3err(X509V3_F_ASIDENTIFIERCHOICE_CANONIZE,
+                      ERR_R_MALLOC_FAILURE);
+            goto done;
+        }
+
+        if ((a_max_plus_one =
+                 BN_to_ASN1_INTEGER(bn, orig = a_max_plus_one)) == NULL) {
+            a_max_plus_one = orig;
+            X509V3err(X509V3_F_ASIDENTIFIERCHOICE_CANONIZE,
+                      ERR_R_MALLOC_FAILURE);
+            goto done;
+        }
+
+        /*
+         * If a and b are adjacent, merge them.
+         */
+        if (ASN1_INTEGER_cmp(a_max_plus_one, b_min) == 0) {
+            ASRange *r;
+            switch (a->type) {
+            case ASIdOrRange_id:
+                if ((r = OPENSSL_malloc(sizeof(*r))) == NULL) {
+                    X509V3err(X509V3_F_ASIDENTIFIERCHOICE_CANONIZE,
+                              ERR_R_MALLOC_FAILURE);
+                    goto done;
+                }
+                r->min = a_min;
+                r->max = b_max;
+                a->type = ASIdOrRange_range;
+                a->u.range = r;
+                break;
+            case ASIdOrRange_range:
+                ASN1_INTEGER_free(a->u.range->max);
+                a->u.range->max = b_max;
+                break;
+            }
+            switch (b->type) {
+            case ASIdOrRange_id:
+                b->u.id = NULL;
+                break;
+            case ASIdOrRange_range:
+                b->u.range->max = NULL;
+                break;
+            }
+            ASIdOrRange_free(b);
+            (void)sk_ASIdOrRange_delete(choice->u.asIdsOrRanges, i + 1);
+            i--;
+            continue;
+        }
+    }
+
+    /*
+     * Check for final inverted range.
+     */
+    i = sk_ASIdOrRange_num(choice->u.asIdsOrRanges) - 1;
+    {
+        ASIdOrRange *a = sk_ASIdOrRange_value(choice->u.asIdsOrRanges, i);
+        ASN1_INTEGER *a_min, *a_max;
+        if (a != NULL && a->type == ASIdOrRange_range) {
+            if (!extract_min_max(a, &a_min, &a_max)
+                    || ASN1_INTEGER_cmp(a_min, a_max) > 0)
+                goto done;
+        }
+    }
+
+    /* Paranoia */
+    if (!ossl_assert(ASIdentifierChoice_is_canonical(choice)))
+        goto done;
+
+    ret = 1;
+
+ done:
+    ASN1_INTEGER_free(a_max_plus_one);
+    BN_free(bn);
+    return ret;
+}
+
+/*
+ * Whack an ASIdentifier extension into canonical form.
+ */
+int X509v3_asid_canonize(ASIdentifiers *asid)
+{
+    return (asid == NULL ||
+            (ASIdentifierChoice_canonize(asid->asnum) &&
+             ASIdentifierChoice_canonize(asid->rdi)));
+}
+
+/*
+ * v2i method for an ASIdentifier extension.
+ */
+static void *v2i_ASIdentifiers(const struct v3_ext_method *method,
+                               struct v3_ext_ctx *ctx,
+                               STACK_OF(CONF_VALUE) *values)
+{
+    ASN1_INTEGER *min = NULL, *max = NULL;
+    ASIdentifiers *asid = NULL;
+    int i;
+
+    if ((asid = ASIdentifiers_new()) == NULL) {
+        X509V3err(X509V3_F_V2I_ASIDENTIFIERS, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    for (i = 0; i < sk_CONF_VALUE_num(values); i++) {
+        CONF_VALUE *val = sk_CONF_VALUE_value(values, i);
+        int i1 = 0, i2 = 0, i3 = 0, is_range = 0, which = 0;
+
+        /*
+         * Figure out whether this is an AS or an RDI.
+         */
+        if (!name_cmp(val->name, "AS")) {
+            which = V3_ASID_ASNUM;
+        } else if (!name_cmp(val->name, "RDI")) {
+            which = V3_ASID_RDI;
+        } else {
+            X509V3err(X509V3_F_V2I_ASIDENTIFIERS,
+                      X509V3_R_EXTENSION_NAME_ERROR);
+            X509V3_conf_err(val);
+            goto err;
+        }
+
+        /*
+         * Handle inheritance.
+         */
+        if (strcmp(val->value, "inherit") == 0) {
+            if (X509v3_asid_add_inherit(asid, which))
+                continue;
+            X509V3err(X509V3_F_V2I_ASIDENTIFIERS,
+                      X509V3_R_INVALID_INHERITANCE);
+            X509V3_conf_err(val);
+            goto err;
+        }
+
+        /*
+         * Number, range, or mistake, pick it apart and figure out which.
+         */
+        i1 = strspn(val->value, "0123456789");
+        if (val->value[i1] == '\0') {
+            is_range = 0;
+        } else {
+            is_range = 1;
+            i2 = i1 + strspn(val->value + i1, " \t");
+            if (val->value[i2] != '-') {
+                X509V3err(X509V3_F_V2I_ASIDENTIFIERS,
+                          X509V3_R_INVALID_ASNUMBER);
+                X509V3_conf_err(val);
+                goto err;
+            }
+            i2++;
+            i2 = i2 + strspn(val->value + i2, " \t");
+            i3 = i2 + strspn(val->value + i2, "0123456789");
+            if (val->value[i3] != '\0') {
+                X509V3err(X509V3_F_V2I_ASIDENTIFIERS,
+                          X509V3_R_INVALID_ASRANGE);
+                X509V3_conf_err(val);
+                goto err;
+            }
+        }
+
+        /*
+         * Syntax is ok, read and add it.
+         */
+        if (!is_range) {
+            if (!X509V3_get_value_int(val, &min)) {
+                X509V3err(X509V3_F_V2I_ASIDENTIFIERS, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+        } else {
+            char *s = OPENSSL_strdup(val->value);
+            if (s == NULL) {
+                X509V3err(X509V3_F_V2I_ASIDENTIFIERS, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+            s[i1] = '\0';
+            min = s2i_ASN1_INTEGER(NULL, s);
+            max = s2i_ASN1_INTEGER(NULL, s + i2);
+            OPENSSL_free(s);
+            if (min == NULL || max == NULL) {
+                X509V3err(X509V3_F_V2I_ASIDENTIFIERS, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+            if (ASN1_INTEGER_cmp(min, max) > 0) {
+                X509V3err(X509V3_F_V2I_ASIDENTIFIERS,
+                          X509V3_R_EXTENSION_VALUE_ERROR);
+                goto err;
+            }
+        }
+        if (!X509v3_asid_add_id_or_range(asid, which, min, max)) {
+            X509V3err(X509V3_F_V2I_ASIDENTIFIERS, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        min = max = NULL;
+    }
+
+    /*
+     * Canonize the result, then we're done.
+     */
+    if (!X509v3_asid_canonize(asid))
+        goto err;
+    return asid;
+
+ err:
+    ASIdentifiers_free(asid);
+    ASN1_INTEGER_free(min);
+    ASN1_INTEGER_free(max);
+    return NULL;
+}
+
+/*
+ * OpenSSL dispatch.
+ */
+const X509V3_EXT_METHOD v3_asid = {
+    NID_sbgp_autonomousSysNum,  /* nid */
+    0,                          /* flags */
+    ASN1_ITEM_ref(ASIdentifiers), /* template */
+    0, 0, 0, 0,                 /* old functions, ignored */
+    0,                          /* i2s */
+    0,                          /* s2i */
+    0,                          /* i2v */
+    v2i_ASIdentifiers,          /* v2i */
+    i2r_ASIdentifiers,          /* i2r */
+    0,                          /* r2i */
+    NULL                        /* extension-specific data */
+};
+
+/*
+ * Figure out whether extension uses inheritance.
+ */
+int X509v3_asid_inherits(ASIdentifiers *asid)
+{
+    return (asid != NULL &&
+            ((asid->asnum != NULL &&
+              asid->asnum->type == ASIdentifierChoice_inherit) ||
+             (asid->rdi != NULL &&
+              asid->rdi->type == ASIdentifierChoice_inherit)));
+}
+
+/*
+ * Figure out whether parent contains child.
+ */
+static int asid_contains(ASIdOrRanges *parent, ASIdOrRanges *child)
+{
+    ASN1_INTEGER *p_min = NULL, *p_max = NULL, *c_min = NULL, *c_max = NULL;
+    int p, c;
+
+    if (child == NULL || parent == child)
+        return 1;
+    if (parent == NULL)
+        return 0;
+
+    p = 0;
+    for (c = 0; c < sk_ASIdOrRange_num(child); c++) {
+        if (!extract_min_max(sk_ASIdOrRange_value(child, c), &c_min, &c_max))
+            return 0;
+        for (;; p++) {
+            if (p >= sk_ASIdOrRange_num(parent))
+                return 0;
+            if (!extract_min_max(sk_ASIdOrRange_value(parent, p), &p_min,
+                                 &p_max))
+                return 0;
+            if (ASN1_INTEGER_cmp(p_max, c_max) < 0)
+                continue;
+            if (ASN1_INTEGER_cmp(p_min, c_min) > 0)
+                return 0;
+            break;
+        }
+    }
+
+    return 1;
+}
+
+/*
+ * Test whether a is a subset of b.
+ */
+int X509v3_asid_subset(ASIdentifiers *a, ASIdentifiers *b)
+{
+    return (a == NULL ||
+            a == b ||
+            (b != NULL &&
+             !X509v3_asid_inherits(a) &&
+             !X509v3_asid_inherits(b) &&
+             asid_contains(b->asnum->u.asIdsOrRanges,
+                           a->asnum->u.asIdsOrRanges) &&
+             asid_contains(b->rdi->u.asIdsOrRanges,
+                           a->rdi->u.asIdsOrRanges)));
+}
+
+/*
+ * Validation error handling via callback.
+ */
+#define validation_err(_err_)           \
+  do {                                  \
+    if (ctx != NULL) {                  \
+      ctx->error = _err_;               \
+      ctx->error_depth = i;             \
+      ctx->current_cert = x;            \
+      ret = ctx->verify_cb(0, ctx);     \
+    } else {                            \
+      ret = 0;                          \
+    }                                   \
+    if (!ret)                           \
+      goto done;                        \
+  } while (0)
+
+/*
+ * Core code for RFC 3779 3.3 path validation.
+ */
+static int asid_validate_path_internal(X509_STORE_CTX *ctx,
+                                       STACK_OF(X509) *chain,
+                                       ASIdentifiers *ext)
+{
+    ASIdOrRanges *child_as = NULL, *child_rdi = NULL;
+    int i, ret = 1, inherit_as = 0, inherit_rdi = 0;
+    X509 *x;
+
+    if (!ossl_assert(chain != NULL && sk_X509_num(chain) > 0)
+            || !ossl_assert(ctx != NULL || ext != NULL)
+            || !ossl_assert(ctx == NULL || ctx->verify_cb != NULL)) {
+        if (ctx != NULL)
+            ctx->error = X509_V_ERR_UNSPECIFIED;
+        return 0;
+    }
+
+
+    /*
+     * Figure out where to start.  If we don't have an extension to
+     * check, we're done.  Otherwise, check canonical form and
+     * set up for walking up the chain.
+     */
+    if (ext != NULL) {
+        i = -1;
+        x = NULL;
+    } else {
+        i = 0;
+        x = sk_X509_value(chain, i);
+        if ((ext = x->rfc3779_asid) == NULL)
+            goto done;
+    }
+    if (!X509v3_asid_is_canonical(ext))
+        validation_err(X509_V_ERR_INVALID_EXTENSION);
+    if (ext->asnum != NULL) {
+        switch (ext->asnum->type) {
+        case ASIdentifierChoice_inherit:
+            inherit_as = 1;
+            break;
+        case ASIdentifierChoice_asIdsOrRanges:
+            child_as = ext->asnum->u.asIdsOrRanges;
+            break;
+        }
+    }
+    if (ext->rdi != NULL) {
+        switch (ext->rdi->type) {
+        case ASIdentifierChoice_inherit:
+            inherit_rdi = 1;
+            break;
+        case ASIdentifierChoice_asIdsOrRanges:
+            child_rdi = ext->rdi->u.asIdsOrRanges;
+            break;
+        }
+    }
+
+    /*
+     * Now walk up the chain.  Extensions must be in canonical form, no
+     * cert may list resources that its parent doesn't list.
+     */
+    for (i++; i < sk_X509_num(chain); i++) {
+        x = sk_X509_value(chain, i);
+        if (!ossl_assert(x != NULL)) {
+            if (ctx != NULL)
+                ctx->error = X509_V_ERR_UNSPECIFIED;
+            return 0;
+        }
+        if (x->rfc3779_asid == NULL) {
+            if (child_as != NULL || child_rdi != NULL)
+                validation_err(X509_V_ERR_UNNESTED_RESOURCE);
+            continue;
+        }
+        if (!X509v3_asid_is_canonical(x->rfc3779_asid))
+            validation_err(X509_V_ERR_INVALID_EXTENSION);
+        if (x->rfc3779_asid->asnum == NULL && child_as != NULL) {
+            validation_err(X509_V_ERR_UNNESTED_RESOURCE);
+            child_as = NULL;
+            inherit_as = 0;
+        }
+        if (x->rfc3779_asid->asnum != NULL &&
+            x->rfc3779_asid->asnum->type ==
+            ASIdentifierChoice_asIdsOrRanges) {
+            if (inherit_as
+                || asid_contains(x->rfc3779_asid->asnum->u.asIdsOrRanges,
+                                 child_as)) {
+                child_as = x->rfc3779_asid->asnum->u.asIdsOrRanges;
+                inherit_as = 0;
+            } else {
+                validation_err(X509_V_ERR_UNNESTED_RESOURCE);
+            }
+        }
+        if (x->rfc3779_asid->rdi == NULL && child_rdi != NULL) {
+            validation_err(X509_V_ERR_UNNESTED_RESOURCE);
+            child_rdi = NULL;
+            inherit_rdi = 0;
+        }
+        if (x->rfc3779_asid->rdi != NULL &&
+            x->rfc3779_asid->rdi->type == ASIdentifierChoice_asIdsOrRanges) {
+            if (inherit_rdi ||
+                asid_contains(x->rfc3779_asid->rdi->u.asIdsOrRanges,
+                              child_rdi)) {
+                child_rdi = x->rfc3779_asid->rdi->u.asIdsOrRanges;
+                inherit_rdi = 0;
+            } else {
+                validation_err(X509_V_ERR_UNNESTED_RESOURCE);
+            }
+        }
+    }
+
+    /*
+     * Trust anchor can't inherit.
+     */
+    if (!ossl_assert(x != NULL)) {
+        if (ctx != NULL)
+            ctx->error = X509_V_ERR_UNSPECIFIED;
+        return 0;
+    }
+    if (x->rfc3779_asid != NULL) {
+        if (x->rfc3779_asid->asnum != NULL &&
+            x->rfc3779_asid->asnum->type == ASIdentifierChoice_inherit)
+            validation_err(X509_V_ERR_UNNESTED_RESOURCE);
+        if (x->rfc3779_asid->rdi != NULL &&
+            x->rfc3779_asid->rdi->type == ASIdentifierChoice_inherit)
+            validation_err(X509_V_ERR_UNNESTED_RESOURCE);
+    }
+
+ done:
+    return ret;
+}
+
+#undef validation_err
+
+/*
+ * RFC 3779 3.3 path validation -- called from X509_verify_cert().
+ */
+int X509v3_asid_validate_path(X509_STORE_CTX *ctx)
+{
+    if (ctx->chain == NULL
+            || sk_X509_num(ctx->chain) == 0
+            || ctx->verify_cb == NULL) {
+        ctx->error = X509_V_ERR_UNSPECIFIED;
+        return 0;
+    }
+    return asid_validate_path_internal(ctx, ctx->chain, NULL);
+}
+
+/*
+ * RFC 3779 3.3 path validation of an extension.
+ * Test whether chain covers extension.
+ */
+int X509v3_asid_validate_resource_set(STACK_OF(X509) *chain,
+                                      ASIdentifiers *ext, int allow_inheritance)
+{
+    if (ext == NULL)
+        return 1;
+    if (chain == NULL || sk_X509_num(chain) == 0)
+        return 0;
+    if (!allow_inheritance && X509v3_asid_inherits(ext))
+        return 0;
+    return asid_validate_path_internal(NULL, chain, ext);
+}
+
+#endif                          /* OPENSSL_NO_RFC3779 */
index aff9f60..3f0081f 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: x509_purp.c,v 1.5 2021/07/23 20:40:49 schwarze Exp $ */
+/* $OpenBSD: x509_purp.c,v 1.6 2021/09/02 12:41:44 job Exp $ */
 /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
  * project 2001.
  */
@@ -366,6 +366,10 @@ X509_supported_extension(X509_EXTENSION *ex)
                NID_basic_constraints,  /* 87 */
                NID_certificate_policies, /* 89 */
                NID_ext_key_usage,      /* 126 */
+#ifndef OPENSSL_NO_RFC3779
+               NID_sbgp_ipAddrBlock,   /* 290 */
+               NID_sbgp_autonomousSysNum, /* 291 */
+#endif
                NID_policy_constraints, /* 401 */
                NID_proxyCertInfo,      /* 663 */
                NID_name_constraints,   /* 666 */
@@ -587,6 +591,15 @@ x509v3_cache_extensions(X509 *x)
                x->ex_flags |= EXFLAG_INVALID;
        setup_crldp(x);
 
+#ifndef OPENSSL_NO_RFC3779
+       x->rfc3779_addr = X509_get_ext_d2i(x, NID_sbgp_ipAddrBlock, &i, NULL);
+       if (x->rfc3779_addr == NULL && i != -1)
+               x->ex_flags |= EXFLAG_INVALID;
+       x->rfc3779_asid = X509_get_ext_d2i(x, NID_sbgp_autonomousSysNum, &i, NULL);
+       if (x->rfc3779_asid == NULL && i != -1)
+               x->ex_flags |= EXFLAG_INVALID;
+#endif
+
        for (i = 0; i < X509_get_ext_count(x); i++) {
                ex = X509_get_ext(x, i);
                if (OBJ_obj2nid(X509_EXTENSION_get_object(ex)) ==
index d2754fa..3cccf86 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: x509v3.h,v 1.2 2020/09/13 15:06:17 beck Exp $ */
+/* $OpenBSD: x509v3.h,v 1.3 2021/09/02 12:41:44 job Exp $ */
 /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
  * project 1999.
  */
@@ -842,6 +842,149 @@ int X509V3_NAME_from_section(X509_NAME *nm, STACK_OF(CONF_VALUE)*dn_sk,
 void X509_POLICY_NODE_print(BIO *out, X509_POLICY_NODE *node, int indent);
 DECLARE_STACK_OF(X509_POLICY_NODE)
 
+#if defined(LIBRESSL_INTERNAL)
+#ifndef OPENSSL_NO_RFC3779
+typedef struct ASRange_st {
+       ASN1_INTEGER *min, *max;
+} ASRange;
+
+# define ASIdOrRange_id          0
+# define ASIdOrRange_range       1
+
+typedef struct ASIdOrRange_st {
+    int type;
+    union {
+        ASN1_INTEGER *id;
+        ASRange *range;
+    } u;
+} ASIdOrRange;
+
+typedef STACK_OF(ASIdOrRange) ASIdOrRanges;
+DEFINE_STACK_OF(ASIdOrRange)
+
+# define ASIdentifierChoice_inherit              0
+# define ASIdentifierChoice_asIdsOrRanges        1
+
+typedef struct ASIdentifierChoice_st {
+    int type;
+    union {
+        ASN1_NULL *inherit;
+        ASIdOrRanges *asIdsOrRanges;
+    } u;
+} ASIdentifierChoice;
+
+typedef struct ASIdentifiers_st {
+    ASIdentifierChoice *asnum, *rdi;
+} ASIdentifiers;
+
+DECLARE_ASN1_FUNCTIONS(ASRange)
+DECLARE_ASN1_FUNCTIONS(ASIdOrRange)
+DECLARE_ASN1_FUNCTIONS(ASIdentifierChoice)
+DECLARE_ASN1_FUNCTIONS(ASIdentifiers)
+typedef struct IPAddressRange_st {
+    ASN1_BIT_STRING *min, *max;
+} IPAddressRange;
+
+# define IPAddressOrRange_addressPrefix  0
+# define IPAddressOrRange_addressRange   1
+
+typedef struct IPAddressOrRange_st {
+    int type;
+    union {
+        ASN1_BIT_STRING *addressPrefix;
+        IPAddressRange *addressRange;
+    } u;
+} IPAddressOrRange;
+
+typedef STACK_OF(IPAddressOrRange) IPAddressOrRanges;
+DEFINE_STACK_OF(IPAddressOrRange)
+
+# define IPAddressChoice_inherit                 0
+# define IPAddressChoice_addressesOrRanges       1
+
+typedef struct IPAddressChoice_st {
+    int type;
+    union {
+        ASN1_NULL *inherit;
+        IPAddressOrRanges *addressesOrRanges;
+    } u;
+} IPAddressChoice;
+
+typedef struct IPAddressFamily_st {
+    ASN1_OCTET_STRING *addressFamily;
+    IPAddressChoice *ipAddressChoice;
+} IPAddressFamily;
+
+typedef STACK_OF(IPAddressFamily) IPAddrBlocks;
+DEFINE_STACK_OF(IPAddressFamily)
+DECLARE_ASN1_FUNCTIONS(IPAddressRange)
+DECLARE_ASN1_FUNCTIONS(IPAddressOrRange)
+DECLARE_ASN1_FUNCTIONS(IPAddressChoice)
+DECLARE_ASN1_FUNCTIONS(IPAddressFamily)
+
+/*
+ * API tag for elements of the ASIdentifer SEQUENCE.
+ */
+# define V3_ASID_ASNUM   0
+# define V3_ASID_RDI     1
+
+/*
+ * AFI values, assigned by IANA.  It'd be nice to make the AFI
+ * handling code totally generic, but there are too many little things
+ * that would need to be defined for other address families for it to
+ * be worth the trouble.
+ */
+# define IANA_AFI_IPV4   1
+# define IANA_AFI_IPV6   2
+/*
+ * Utilities to construct and extract values from RFC3779 extensions,
+ * since some of the encodings (particularly for IP address prefixes
+ * and ranges) are a bit tedious to work with directly.
+ */
+int X509v3_asid_add_inherit(ASIdentifiers *asid, int which);
+int X509v3_asid_add_id_or_range(ASIdentifiers *asid, int which,
+                                ASN1_INTEGER *min, ASN1_INTEGER *max);
+int X509v3_addr_add_inherit(IPAddrBlocks *addr,
+                            const unsigned afi, const unsigned *safi);
+int X509v3_addr_add_prefix(IPAddrBlocks *addr,
+                           const unsigned afi, const unsigned *safi,
+                           unsigned char *a, const int prefixlen);
+int X509v3_addr_add_range(IPAddrBlocks *addr,
+                          const unsigned afi, const unsigned *safi,
+                          unsigned char *min, unsigned char *max);
+unsigned X509v3_addr_get_afi(const IPAddressFamily *f);
+int X509v3_addr_get_range(IPAddressOrRange *aor, const unsigned afi,
+                          unsigned char *min, unsigned char *max,
+                          const int length);
+/*
+ * Canonical forms.
+ */
+int X509v3_asid_is_canonical(ASIdentifiers *asid);
+int X509v3_addr_is_canonical(IPAddrBlocks *addr);
+int X509v3_asid_canonize(ASIdentifiers *asid);
+int X509v3_addr_canonize(IPAddrBlocks *addr);
+
+/*
+ * Tests for inheritance and containment.
+ */
+int X509v3_asid_inherits(ASIdentifiers *asid);
+int X509v3_addr_inherits(IPAddrBlocks *addr);
+int X509v3_asid_subset(ASIdentifiers *a, ASIdentifiers *b);
+int X509v3_addr_subset(IPAddrBlocks *a, IPAddrBlocks *b);
+
+/*
+ * Check whether RFC 3779 extensions nest properly in chains.
+ */
+int X509v3_asid_validate_path(X509_STORE_CTX *);
+int X509v3_addr_validate_path(X509_STORE_CTX *);
+int X509v3_asid_validate_resource_set(STACK_OF(X509) *chain,
+                                      ASIdentifiers *ext,
+                                      int allow_inheritance);
+int X509v3_addr_validate_resource_set(STACK_OF(X509) *chain,
+                                      IPAddrBlocks *ext, int allow_inheritance);
+
+#endif                         /* OPENSSL_NO_RFC3779 */
+#endif
 
 /* BEGIN ERROR CODES */
 /* The following lines are auto generated by the script mkerr.pl. Any changes