Commit f08174e914 for openssl.org

commit f08174e91476e1ac9b9bcf79c7d33b11f22b0582
Author: herbenderbler <johnclaus@gmail.com>
Date:   Sat Mar 28 21:53:06 2026 -0600

    Add public API for IPAddrBlocks (RFC 3779)

    Fixes #18528

    - Add IPAddrBlocks_new(), IPAddrBlocks_free(), d2i_IPAddrBlocks(),
      i2d_IPAddrBlocks(), and IPAddrBlocks_it to the public API.

    Reviewed-by: Andrew Dinh <andrewd@openssl.org>
    Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
    Reviewed-by: Neil Horman <nhorman@openssl.org>
    MergeDate: Thu Sep 10 21:09:33 2026
    (Merged from https://github.com/openssl/openssl/pull/30430)

diff --git a/CHANGES.md b/CHANGES.md
index 24bed46e86..a1a1e6e1e3 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -456,6 +456,14 @@ OpenSSL 4.1

    *Adam Tabak*

+ * Added public API for IPAddrBlocks (RFC 3779), mirroring the existing
+   ASIdentifiers API: `IPAddrBlocks_new()`, `IPAddrBlocks_free()`,
+   `d2i_IPAddrBlocks()`, `i2d_IPAddrBlocks()`, and the exported
+   `IPAddrBlocks_it` ASN.1 item for use in custom ASN.1 templates
+   (e.g. RPKI Signed Checklist).  Fixes issue #18528.
+
+   *John Claus*
+
  * Fixed X.509 verification of certificate chains that use DSA signatures
    with SHA-384 or SHA-512 by registering `dsa_with_SHA384` and
    `dsa_with_SHA512` in the signature-algorithm cross-reference table.
diff --git a/crypto/x509/v3_addr.c b/crypto/x509/v3_addr.c
index 73ad2345b9..8bf6fed69e 100644
--- a/crypto/x509/v3_addr.c
+++ b/crypto/x509/v3_addr.c
@@ -55,9 +55,11 @@ ASN1_SEQUENCE(IPAddressFamily) = {

 ASN1_ITEM_TEMPLATE(IPAddrBlocks) = ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0,
     IPAddrBlocks, IPAddressFamily)
-static_ASN1_ITEM_TEMPLATE_END(IPAddrBlocks)
+ASN1_ITEM_TEMPLATE_END(IPAddrBlocks)

-    IMPLEMENT_ASN1_FUNCTIONS(IPAddressRange)
+IMPLEMENT_ASN1_FUNCTIONS_fname(IPAddrBlocks, IPAddrBlocks, IPAddrBlocks)
+
+IMPLEMENT_ASN1_FUNCTIONS(IPAddressRange)
 IMPLEMENT_ASN1_FUNCTIONS(IPAddressOrRange)
 IMPLEMENT_ASN1_FUNCTIONS(IPAddressChoice)
 IMPLEMENT_ASN1_FUNCTIONS(IPAddressFamily)
diff --git a/doc/man3/X509_dup.pod b/doc/man3/X509_dup.pod
index d0b649738b..59fe73a082 100644
--- a/doc/man3/X509_dup.pod
+++ b/doc/man3/X509_dup.pod
@@ -92,6 +92,9 @@ IPAddressOrRange_free,
 IPAddressOrRange_new,
 IPAddressRange_free,
 IPAddressRange_new,
+IPAddrBlocks_free,
+IPAddrBlocks_it,
+IPAddrBlocks_new,
 ISSUER_SIGN_TOOL_free,
 ISSUER_SIGN_TOOL_it,
 ISSUER_SIGN_TOOL_new,
@@ -604,6 +607,9 @@ CMS_EnvelopedData_dup(), OSSL_CRMF_ENCRYPTEDKEY_free(),
 OSSL_CRMF_ENCRYPTEDKEY_it() and OSSL_CRMF_ENCRYPTEDKEY_new()
 were added in OpenSSL 3.5.

+IPAddrBlocks_free(), IPAddrBlocks_it() and IPAddrBlocks_new()
+were added in OpenSSL 4.1.
+
 =head1 COPYRIGHT

 Copyright 2016-2025 The OpenSSL Project Authors. All Rights Reserved.
diff --git a/doc/man3/d2i_X509.pod b/doc/man3/d2i_X509.pod
index aceddddf2f..b37855da16 100644
--- a/doc/man3/d2i_X509.pod
+++ b/doc/man3/d2i_X509.pod
@@ -67,6 +67,7 @@ d2i_IPAddressChoice,
 d2i_IPAddressFamily,
 d2i_IPAddressOrRange,
 d2i_IPAddressRange,
+d2i_IPAddrBlocks,
 d2i_ISSUER_SIGN_TOOL,
 d2i_ISSUING_DIST_POINT,
 d2i_NAMING_AUTHORITY,
@@ -281,6 +282,7 @@ i2d_IPAddressChoice,
 i2d_IPAddressFamily,
 i2d_IPAddressOrRange,
 i2d_IPAddressRange,
+i2d_IPAddrBlocks,
 i2d_ISSUER_SIGN_TOOL,
 i2d_ISSUING_DIST_POINT,
 i2d_NAMING_AUTHORITY,
@@ -765,6 +767,8 @@ i2d_OSSL_TIME_SPEC_TIME(), i2d_OSSL_TIME_SPEC_WEEKS(),
 i2d_OSSL_TIME_SPEC_X_DAY_OF()
 were added in OpenSSL 3.5.

+d2i_IPAddrBlocks() and i2d_IPAddrBlocks() were added in OpenSSL 4.1.
+
 =head1 COPYRIGHT

 Copyright 1998-2026 The OpenSSL Project Authors. All Rights Reserved.
diff --git a/include/openssl/x509v3.h.in b/include/openssl/x509v3.h.in
index 21def64b94..34f1c6e69b 100644
--- a/include/openssl/x509v3.h.in
+++ b/include/openssl/x509v3.h.in
@@ -931,6 +931,7 @@ DECLARE_ASN1_FUNCTIONS(IPAddressRange)
 DECLARE_ASN1_FUNCTIONS(IPAddressOrRange)
 DECLARE_ASN1_FUNCTIONS(IPAddressChoice)
 DECLARE_ASN1_FUNCTIONS(IPAddressFamily)
+DECLARE_ASN1_FUNCTIONS(IPAddrBlocks)

 /*
  * API tag for elements of the ASIdentifier SEQUENCE.
diff --git a/test/v3ext.c b/test/v3ext.c
index 9a47f0ed5b..aa55b36702 100644
--- a/test/v3ext.c
+++ b/test/v3ext.c
@@ -1262,6 +1262,70 @@ end:
     return ret;
 }

+/*
+ * Regression test for IPAddrBlocks_new/free and d2i/i2d_IPAddrBlocks (issue #18528).
+ * Ensures empty and non-empty IPAddrBlocks round-trip correctly.
+ */
+static int test_ipaddrblocks_api(void)
+{
+    IPAddrBlocks *addr = NULL, *decoded = NULL;
+    ASN1_OCTET_STRING *ip1 = NULL, *ip2 = NULL;
+    unsigned char *der = NULL, *derp;
+    int len;
+    int ret = 0;
+
+    /* Round-trip empty IPAddrBlocks */
+    addr = IPAddrBlocks_new();
+    if (!TEST_ptr(addr))
+        goto end;
+    len = i2d_IPAddrBlocks(addr, &der);
+    if (!TEST_int_ge(len, 0) || !TEST_ptr(der))
+        goto end;
+    derp = der;
+    decoded = d2i_IPAddrBlocks(NULL, (const unsigned char **)&derp, len);
+    if (!TEST_ptr(decoded) || !TEST_int_eq(sk_IPAddressFamily_num(decoded), 0))
+        goto end;
+    IPAddrBlocks_free(addr);
+    IPAddrBlocks_free(decoded);
+    OPENSSL_free(der);
+    addr = decoded = NULL;
+    der = NULL;
+
+    /* Round-trip non-empty IPAddrBlocks and verify structure */
+    addr = IPAddrBlocks_new();
+    if (!TEST_ptr(addr))
+        goto end;
+    if (!TEST_true(X509v3_addr_canonize(addr)))
+        goto end;
+    ip1 = a2i_IPADDRESS(ranges[0].ip1);
+    ip2 = a2i_IPADDRESS(ranges[0].ip2);
+    if (!TEST_ptr(ip1) || !TEST_ptr(ip2))
+        goto end;
+    if (!TEST_true(X509v3_addr_add_range(addr, ranges[0].afi, NULL, ip1->data, ip2->data)))
+        goto end;
+    if (!TEST_true(X509v3_addr_is_canonical(addr)))
+        goto end;
+
+    len = i2d_IPAddrBlocks(addr, &der);
+    if (!TEST_int_ge(len, 0) || !TEST_ptr(der))
+        goto end;
+    derp = der;
+    decoded = d2i_IPAddrBlocks(NULL, (const unsigned char **)&derp, len);
+    if (!TEST_ptr(decoded))
+        goto end;
+    if (!check_addr(decoded, ranges[0].rorp))
+        goto end;
+
+    ret = 1;
+end:
+    IPAddrBlocks_free(addr);
+    IPAddrBlocks_free(decoded);
+    OPENSSL_free(der);
+    ASN1_OCTET_STRING_free(ip1);
+    ASN1_OCTET_STRING_free(ip2);
+    return ret;
+}
+
 #endif /* OPENSSL_NO_RFC3779 */

 OPT_TEST_DECLARE_USAGE("cert.pem\n")
@@ -1296,6 +1360,7 @@ int setup_tests(void)
     ADD_TEST(test_addr_canonize_failure_then_inspect);
     ADD_TEST(test_asid_range_merge_canonize);
     ADD_TEST(test_asid_canonize_inverted_midsweep);
+    ADD_TEST(test_ipaddrblocks_api);
 #endif /* OPENSSL_NO_RFC3779 */
     return 1;
 }
diff --git a/util/libcrypto.num b/util/libcrypto.num
index a2ff5af75e..fe2d6afb4f 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -5733,3 +5733,8 @@ ASN1_STRING_set1_string                 ?	4_1_0	EXIST::FUNCTION:
 ASN1_STRING_get_length                  ?	4_1_0	EXIST::FUNCTION:
 CMS_add_standard_smimecap_ex            ?	4_1_0	EXIST::FUNCTION:CMS
 BIO_socket_ready                        ?	4_1_0	EXIST::FUNCTION:SOCK
+d2i_IPAddrBlocks                        ?	4_1_0	EXIST::FUNCTION:RFC3779
+i2d_IPAddrBlocks                        ?	4_1_0	EXIST::FUNCTION:RFC3779
+IPAddrBlocks_free                       ?	4_1_0	EXIST::FUNCTION:RFC3779
+IPAddrBlocks_new                        ?	4_1_0	EXIST::FUNCTION:RFC3779
+IPAddrBlocks_it                         ?	4_1_0	EXIST::FUNCTION:RFC3779