From: Alan T. DeKok Date: Tue, 25 Feb 2025 21:25:54 +0000 (-0500) Subject: encode and decode IPv4 and IPv6 prefixes X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3ef0e32f385564471b43f2f4113b9531cc1b2ed0;p=thirdparty%2Ffreeradius-server.git encode and decode IPv4 and IPv6 prefixes --- diff --git a/src/protocols/der/base.c b/src/protocols/der/base.c index 8d082bc76ff..dc27383329f 100644 --- a/src/protocols/der/base.c +++ b/src/protocols/der/base.c @@ -108,10 +108,18 @@ static const bool *fr_type_to_der_tags[FR_DER_TAG_MAX] = { [FR_DER_TAG_BITSTRING] = true, }, + [FR_TYPE_IPV4_PREFIX] = (bool [FR_DER_TAG_MAX]) { + [FR_DER_TAG_BITSTRING] = true, + }, + [FR_TYPE_IPV6_ADDR] = (bool [FR_DER_TAG_MAX]) { [FR_DER_TAG_BITSTRING] = true, }, + [FR_TYPE_IPV6_PREFIX] = (bool [FR_DER_TAG_MAX]) { + [FR_DER_TAG_BITSTRING] = true, + }, + [FR_TYPE_BOOL] = (bool [FR_DER_TAG_MAX]) { [FR_DER_TAG_BOOLEAN] = true, [FR_DER_TAG_INTEGER] = true, @@ -595,8 +603,6 @@ static bool type_parse(fr_type_t *type_p,fr_dict_attr_t **da_p, char const *name fr_strerror_const("Cannot use 'tlv' in DER. Please use 'sequence'"); return false; - case FR_TYPE_IPV4_PREFIX: - case FR_TYPE_IPV6_PREFIX: case FR_TYPE_IFID: case FR_TYPE_COMBO_IP_ADDR: case FR_TYPE_COMBO_IP_PREFIX: @@ -687,7 +693,9 @@ static const fr_der_tag_t fr_type_to_der_tag_defaults[FR_TYPE_MAX + 1] = { [FR_TYPE_STRING] = FR_DER_TAG_UTF8_STRING, [FR_TYPE_IPV4_ADDR] = FR_DER_TAG_BITSTRING, + [FR_TYPE_IPV4_PREFIX] = FR_DER_TAG_BITSTRING, [FR_TYPE_IPV6_ADDR] = FR_DER_TAG_BITSTRING, + [FR_TYPE_IPV6_PREFIX] = FR_DER_TAG_BITSTRING, [FR_TYPE_BOOL] = FR_DER_TAG_BOOLEAN, diff --git a/src/protocols/der/decode.c b/src/protocols/der/decode.c index 4977beff0e7..9e06b32fcfc 100644 --- a/src/protocols/der/decode.c +++ b/src/protocols/der/decode.c @@ -1452,6 +1452,20 @@ static ssize_t fr_der_decode_ipv4_addr(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_ fr_pair_t *vp; fr_dbuff_t our_in = FR_DBUFF(in); + /* + * RFC3779 Section 2.1.1. + * + * An IP address or prefix is encoded in the IP address delegation + * extension as a DER-encoded ASN.1 BIT STRING containing the constant + * most-significant bits. Recall [X.690] that the DER encoding of a BIT + * STRING consists of the BIT STRING type (0x03), followed by (an + * encoding of) the number of value octets, followed by the value. The + * value consists of an "initial octet" that specifies the number of + * unused bits in the last value octet, followed by the "subsequent + * octets" that contain the octets of the bit string. (For IP + * addresses, the encoding of the length will be just the length.) + */ + if (fr_dbuff_remaining(&our_in) != 1 + sizeof(vp->vp_ipv4addr)) { fr_strerror_printf("Invalid ipv4addr size. Expected %zu, got %zu", 1 + sizeof(vp->vp_ipv4addr), fr_dbuff_remaining(&our_in)); @@ -1479,6 +1493,57 @@ static ssize_t fr_der_decode_ipv4_addr(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_ return fr_dbuff_set(in, &our_in); } +static ssize_t fr_der_decode_ipv4_prefix(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, + fr_dbuff_t *in, UNUSED fr_der_decode_ctx_t *decode_ctx) +{ + uint8_t byte; + fr_pair_t *vp; + fr_dbuff_t our_in = FR_DBUFF(in); + size_t len = fr_dbuff_remaining(&our_in); + + /* + * RFC3779 Section 2.1.1. + * + * An IP address or prefix is encoded in the IP address delegation + * extension as a DER-encoded ASN.1 BIT STRING containing the constant + * most-significant bits. Recall [X.690] that the DER encoding of a BIT + * STRING consists of the BIT STRING type (0x03), followed by (an + * encoding of) the number of value octets, followed by the value. The + * value consists of an "initial octet" that specifies the number of + * unused bits in the last value octet, followed by the "subsequent + * octets" that contain the octets of the bit string. (For IP + * addresses, the encoding of the length will be just the length.) + */ + + if (!len || (len > 1 + sizeof(vp->vp_ipv4addr))) { + fr_strerror_printf("Invalid ipv4prefix size. Expected 1..%zu, got %zu", + 1 + sizeof(vp->vp_ipv4addr), len); + return -1; + } + len--; + + FR_DBUFF_OUT_RETURN(&byte, &our_in); + if (byte > 7) { + fr_strerror_printf("Invalid ipv4prefix is too large (%02x)", byte); + return -1; + } + + vp = fr_pair_afrom_da(ctx, parent); + if (unlikely(!vp)) { + fr_strerror_const("Out of memory"); + return -1; + } + + vp->vp_ip.af = AF_INET; + vp->vp_ip.prefix = len * 8 - byte; + + if (len) FR_DBUFF_OUT_MEMCPY_RETURN((uint8_t *) &vp->vp_ipv4addr, &our_in, len); + + fr_pair_append(out, vp); + + return fr_dbuff_set(in, &our_in); +} + static ssize_t fr_der_decode_ipv6_addr(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, fr_dbuff_t *in, UNUSED fr_der_decode_ctx_t *decode_ctx) { @@ -1486,6 +1551,20 @@ static ssize_t fr_der_decode_ipv6_addr(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_ fr_pair_t *vp; fr_dbuff_t our_in = FR_DBUFF(in); + /* + * RFC3779 Section 2.1.1. + * + * An IP address or prefix is encoded in the IP address delegation + * extension as a DER-encoded ASN.1 BIT STRING containing the constant + * most-significant bits. Recall [X.690] that the DER encoding of a BIT + * STRING consists of the BIT STRING type (0x03), followed by (an + * encoding of) the number of value octets, followed by the value. The + * value consists of an "initial octet" that specifies the number of + * unused bits in the last value octet, followed by the "subsequent + * octets" that contain the octets of the bit string. (For IP + * addresses, the encoding of the length will be just the length.) + */ + if (fr_dbuff_remaining(&our_in) != 1 + sizeof(vp->vp_ipv6addr)) { fr_strerror_printf("Invalid ipv6addr size. Expected %zu, got %zu", 1 + sizeof(vp->vp_ipv6addr), fr_dbuff_remaining(&our_in)); @@ -1513,6 +1592,57 @@ static ssize_t fr_der_decode_ipv6_addr(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_ return fr_dbuff_set(in, &our_in); } +static ssize_t fr_der_decode_ipv6_prefix(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, + fr_dbuff_t *in, UNUSED fr_der_decode_ctx_t *decode_ctx) +{ + uint8_t byte; + fr_pair_t *vp; + fr_dbuff_t our_in = FR_DBUFF(in); + size_t len = fr_dbuff_remaining(&our_in); + + /* + * RFC3779 Section 2.1.1. + * + * An IP address or prefix is encoded in the IP address delegation + * extension as a DER-encoded ASN.1 BIT STRING containing the constant + * most-significant bits. Recall [X.690] that the DER encoding of a BIT + * STRING consists of the BIT STRING type (0x03), followed by (an + * encoding of) the number of value octets, followed by the value. The + * value consists of an "initial octet" that specifies the number of + * unused bits in the last value octet, followed by the "subsequent + * octets" that contain the octets of the bit string. (For IP + * addresses, the encoding of the length will be just the length.) + */ + + if (!len || (len > 1 + sizeof(vp->vp_ipv6addr))) { + fr_strerror_printf("Invalid ipv6prefix size. Expected 1..%zu, got %zu", + 1 + sizeof(vp->vp_ipv6addr), len); + return -1; + } + len--; + + FR_DBUFF_OUT_RETURN(&byte, &our_in); + if (byte > 7) { + fr_strerror_printf("Invalid ipv6prefix is too large (%02x)", byte); + return -1; + } + + vp = fr_pair_afrom_da(ctx, parent); + if (unlikely(!vp)) { + fr_strerror_const("Out of memory"); + return -1; + } + + vp->vp_ip.af = AF_INET; + vp->vp_ip.prefix = len * 8 - byte; + + if (len) FR_DBUFF_OUT_MEMCPY_RETURN((uint8_t *) &vp->vp_ipv6addr, &our_in, len); + + fr_pair_append(out, vp); + + return fr_dbuff_set(in, &our_in); +} + static const fr_der_tag_decode_t tag_funcs[FR_DER_TAG_MAX] = { [FR_DER_TAG_BOOLEAN] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_boolean }, [FR_DER_TAG_INTEGER] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_integer }, @@ -1538,7 +1668,9 @@ static const fr_der_tag_decode_t tag_funcs[FR_DER_TAG_MAX] = { static const fr_der_tag_decode_t type_funcs[FR_TYPE_MAX] = { [FR_TYPE_IPV4_ADDR] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_ipv4_addr }, + [FR_TYPE_IPV4_PREFIX] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_ipv4_prefix }, [FR_TYPE_IPV6_ADDR] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_ipv6_addr }, + [FR_TYPE_IPV6_PREFIX] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_ipv6_prefix }, }; /** Decode the tag and length fields of a DER encoded structure @@ -1626,18 +1758,8 @@ static ssize_t fr_der_decode_hdr(fr_dict_attr_t const *parent, fr_dbuff_t *in, u return -1; } - if (parent) switch (parent->type) { - case FR_TYPE_IPV4_ADDR: - case FR_TYPE_IPV6_ADDR: - func = &type_funcs[parent->type]; - break; - - default: - func = &tag_funcs[*tag]; - break; - } else { - func = &tag_funcs[*tag]; - } + func = &tag_funcs[*tag]; + fr_assert(func != NULL); /* * Check if the tag is an OID. OID tags will be handled differently @@ -2370,16 +2492,8 @@ static ssize_t fr_der_decode_pair_dbuff(TALLOC_CTX *ctx, fr_pair_list_t *out, fr return fr_dbuff_set(in, &our_in); } - switch (parent->type) { - case FR_TYPE_IPV4_ADDR: - case FR_TYPE_IPV6_ADDR: - func = &type_funcs[parent->type]; - break; - - default: - func = &tag_funcs[tag]; - break; - } + func = &type_funcs[parent->type]; + if (!func->decode) func = &tag_funcs[tag]; fr_assert(func != NULL); /* @@ -2436,6 +2550,8 @@ static ssize_t fr_der_decode_pair_dbuff(TALLOC_CTX *ctx, fr_pair_list_t *out, fr } if (tag != FR_DER_TAG_OID) { + fr_assert(func->decode != NULL); + /* * The decode function can return 0 if len==0. This is true for 'null' data types, and * for variable-sized types such as strings. diff --git a/src/protocols/der/encode.c b/src/protocols/der/encode.c index e1a9befbd73..4c9152cfb9c 100644 --- a/src/protocols/der/encode.c +++ b/src/protocols/der/encode.c @@ -386,6 +386,43 @@ static ssize_t fr_der_encode_ipv4_addr(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, return fr_dbuff_set(dbuff, &our_dbuff); } +static ssize_t fr_der_encode_ipv4_prefix(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, UNUSED fr_der_encode_ctx_t *encode_ctx) +{ + fr_dbuff_t our_dbuff = FR_DBUFF(dbuff); + fr_pair_t const *vp; + size_t len; + + vp = fr_dcursor_current(cursor); + PAIR_VERIFY(vp); + fr_assert(!vp->da->flags.is_raw); + + if (vp->vp_ip.prefix == 0) { + FR_DBUFF_IN_RETURN(&our_dbuff, (uint8_t) 0x00); + return fr_dbuff_set(dbuff, &our_dbuff); + } + + /* + * RFC3779 Section 2.1.1. + * + * An IP address or prefix is encoded in the IP address delegation + * extension as a DER-encoded ASN.1 BIT STRING containing the constant + * most-significant bits. Recall [X.690] that the DER encoding of a BIT + * STRING consists of the BIT STRING type (0x03), followed by (an + * encoding of) the number of value octets, followed by the value. The + * value consists of an "initial octet" that specifies the number of + * unused bits in the last value octet, followed by the "subsequent + * octets" that contain the octets of the bit string. (For IP + * addresses, the encoding of the length will be just the length.) + */ + FR_DBUFF_IN_RETURN(&our_dbuff, (uint8_t) (8 - (vp->vp_ip.prefix & 0x07))); + + len = (vp->vp_ip.prefix + 0x07) >> 3; + + if (len) FR_DBUFF_IN_MEMCPY_RETURN(&our_dbuff, (uint8_t const *) &vp->vp_ipv4addr, len); + + return fr_dbuff_set(dbuff, &our_dbuff); +} + static ssize_t fr_der_encode_ipv6_addr(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, UNUSED fr_der_encode_ctx_t *encode_ctx) { fr_dbuff_t our_dbuff = FR_DBUFF(dbuff); @@ -418,6 +455,38 @@ static ssize_t fr_der_encode_ipv6_addr(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, return fr_dbuff_set(dbuff, &our_dbuff); } +static ssize_t fr_der_encode_ipv6_prefix(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, UNUSED fr_der_encode_ctx_t *encode_ctx) +{ + fr_dbuff_t our_dbuff = FR_DBUFF(dbuff); + fr_pair_t const *vp; + size_t len; + + vp = fr_dcursor_current(cursor); + PAIR_VERIFY(vp); + fr_assert(!vp->da->flags.is_raw); + + /* + * RFC3779 Section 2.1.1. + * + * An IP address or prefix is encoded in the IP address delegation + * extension as a DER-encoded ASN.1 BIT STRING containing the constant + * most-significant bits. Recall [X.690] that the DER encoding of a BIT + * STRING consists of the BIT STRING type (0x03), followed by (an + * encoding of) the number of value octets, followed by the value. The + * value consists of an "initial octet" that specifies the number of + * unused bits in the last value octet, followed by the "subsequent + * octets" that contain the octets of the bit string. (For IP + * addresses, the encoding of the length will be just the length.) + */ + + FR_DBUFF_IN_RETURN(&our_dbuff, (uint8_t) (8 - (vp->vp_ip.prefix & 0x07))); + + len = (vp->vp_ip.prefix + 0x07) >> 3; + + if (len) FR_DBUFF_IN_MEMCPY_RETURN(&our_dbuff, (uint8_t const *) &vp->vp_ipv6addr, len); + + return fr_dbuff_set(dbuff, &our_dbuff); +} static ssize_t fr_der_encode_octetstring(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, @@ -1576,7 +1645,9 @@ static const fr_der_tag_encode_t tag_funcs[FR_DER_TAG_MAX] = { static const fr_der_tag_encode_t type_funcs[FR_TYPE_MAX] = { [FR_TYPE_IPV4_ADDR] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_ipv4_addr }, + [FR_TYPE_IPV4_PREFIX] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_ipv4_prefix }, [FR_TYPE_IPV6_ADDR] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_ipv6_addr }, + [FR_TYPE_IPV6_PREFIX] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_ipv6_prefix }, }; @@ -1810,16 +1881,10 @@ static ssize_t encode_value(fr_dbuff_t *dbuff, UNUSED fr_da_stack_t *da_stack, U fr_assert(tag < FR_DER_TAG_MAX); - switch (vp->vp_type) { - case FR_TYPE_IPV4_ADDR: - case FR_TYPE_IPV6_ADDR: - func = &type_funcs[tag]; - break; + func = &type_funcs[vp->vp_type]; + if (!func->encode) func = &tag_funcs[tag]; - default: - func = &tag_funcs[tag]; - break; - } + fr_assert(func != NULL); fr_assert(func->encode != NULL); /* diff --git a/src/tests/unit/protocols/der/base.txt b/src/tests/unit/protocols/der/base.txt index c7750ed338b..7c1cf91c93f 100644 --- a/src/tests/unit/protocols/der/base.txt +++ b/src/tests/unit/protocols/der/base.txt @@ -884,5 +884,24 @@ proto-dictionary-root Test-IPv4-Address decode-pair 03 05 00 0a 05 00 04 match Test-IPv4-Address = 10.5.0.4 +proto-dictionary der + +encode-pair Test-IPv4-Prefix = 10.5.0.0/23 +match 03 04 01 0a 05 00 + +# +# Just the length byte +# +encode-pair Test-IPv4-Prefix = 0/0 +match 03 01 00 + +proto-dictionary-root Test-IPv4-Prefix + +decode-pair 03 04 01 0a 05 00 +match Test-IPv4-Prefix = 10.5.0.0/23 + +decode-pair 03 01 00 +match Test-IPv4-Prefix = 0.0.0.0/0 + count -match 535 +match 545 diff --git a/src/tests/unit/protocols/der/dictionary.test b/src/tests/unit/protocols/der/dictionary.test index 7ce33f51eee..06e8971adca 100644 --- a/src/tests/unit/protocols/der/dictionary.test +++ b/src/tests/unit/protocols/der/dictionary.test @@ -31,7 +31,9 @@ DEFINE Test-Boolean bool DEFINE Test-Integer integer DEFINE Test-IPv4-Address ipv4addr +DEFINE Test-IPv4-Prefix ipv4prefix DEFINE Test-IPv6-Address ipv4addr +DEFINE Test-IPv6-Prefix ipv4prefix DEFINE Foo sequence sequence_of=integer BEGIN Foo