From: Alan T. DeKok Date: Tue, 25 Feb 2025 20:01:06 +0000 (-0500) Subject: Add encode / decode of ipv4add and ipv6addr X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=05d041f276a571063802d7eb3e48ab343874bb11;p=thirdparty%2Ffreeradius-server.git Add encode / decode of ipv4add and ipv6addr RFC 3779 defines a format for IP addresses, and IP prefixes --- diff --git a/src/protocols/der/base.c b/src/protocols/der/base.c index 457b844ffd8..8d082bc76ff 100644 --- a/src/protocols/der/base.c +++ b/src/protocols/der/base.c @@ -104,6 +104,14 @@ bool fr_der_tags_compatible(fr_der_tag_t tag1, fr_der_tag_t tag2) * Create a mapping between FR_TYPE_* and valid FR_DER_TAG_*'s */ static const bool *fr_type_to_der_tags[FR_DER_TAG_MAX] = { + [FR_TYPE_IPV4_ADDR] = (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_BOOL] = (bool [FR_DER_TAG_MAX]) { [FR_DER_TAG_BOOLEAN] = true, [FR_DER_TAG_INTEGER] = true, @@ -587,9 +595,7 @@ 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_ADDR: case FR_TYPE_IPV4_PREFIX: - case FR_TYPE_IPV6_ADDR: case FR_TYPE_IPV6_PREFIX: case FR_TYPE_IFID: case FR_TYPE_COMBO_IP_ADDR: @@ -677,7 +683,14 @@ static bool type_parse(fr_type_t *type_p,fr_dict_attr_t **da_p, char const *name } static const fr_der_tag_t fr_type_to_der_tag_defaults[FR_TYPE_MAX + 1] = { + [FR_TYPE_OCTETS] = FR_DER_TAG_OCTETSTRING, + [FR_TYPE_STRING] = FR_DER_TAG_UTF8_STRING, + + [FR_TYPE_IPV4_ADDR] = FR_DER_TAG_BITSTRING, + [FR_TYPE_IPV6_ADDR] = FR_DER_TAG_BITSTRING, + [FR_TYPE_BOOL] = FR_DER_TAG_BOOLEAN, + [FR_TYPE_UINT8] = FR_DER_TAG_INTEGER, [FR_TYPE_UINT16] = FR_DER_TAG_INTEGER, [FR_TYPE_UINT32] = FR_DER_TAG_INTEGER, @@ -686,8 +699,6 @@ static const fr_der_tag_t fr_type_to_der_tag_defaults[FR_TYPE_MAX + 1] = { [FR_TYPE_INT16] = FR_DER_TAG_INTEGER, [FR_TYPE_INT32] = FR_DER_TAG_INTEGER, [FR_TYPE_INT64] = FR_DER_TAG_INTEGER, - [FR_TYPE_OCTETS] = FR_DER_TAG_OCTETSTRING, - [FR_TYPE_STRING] = FR_DER_TAG_UTF8_STRING, [FR_TYPE_DATE] = FR_DER_TAG_GENERALIZED_TIME, [FR_TYPE_TLV] = FR_DER_TAG_SEQUENCE, [FR_TYPE_STRUCT] = FR_DER_TAG_SEQUENCE, diff --git a/src/protocols/der/decode.c b/src/protocols/der/decode.c index c14273ac526..4977beff0e7 100644 --- a/src/protocols/der/decode.c +++ b/src/protocols/der/decode.c @@ -82,84 +82,6 @@ typedef struct { static ssize_t fr_der_decode_pair_dbuff(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, fr_dbuff_t *in, fr_der_decode_ctx_t *decode_ctx) CC_HINT(nonnull); -static ssize_t fr_der_decode_boolean(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, fr_dbuff_t *in, - fr_der_decode_ctx_t *decode_ctx) CC_HINT(nonnull); - -static ssize_t fr_der_decode_integer(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, fr_dbuff_t *in, - fr_der_decode_ctx_t *decode_ctx) CC_HINT(nonnull); - -static ssize_t fr_der_decode_bitstring(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, - fr_dbuff_t *in, fr_der_decode_ctx_t *decode_ctx) CC_HINT(nonnull); - -static ssize_t fr_der_decode_octetstring(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, - fr_dbuff_t *in, fr_der_decode_ctx_t *decode_ctx) CC_HINT(nonnull); - -static ssize_t fr_der_decode_null(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, fr_dbuff_t *in, - fr_der_decode_ctx_t *decode_ctx) CC_HINT(nonnull); - -static ssize_t fr_der_decode_utf8_string(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, - fr_dbuff_t *in, fr_der_decode_ctx_t *decode_ctx) CC_HINT(nonnull); - -static ssize_t fr_der_decode_sequence(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, - fr_dbuff_t *in, fr_der_decode_ctx_t *decode_ctx) CC_HINT(nonnull); - -static ssize_t fr_der_decode_set(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, fr_dbuff_t *in, - fr_der_decode_ctx_t *decode_ctx) CC_HINT(nonnull); - -static ssize_t fr_der_decode_printable_string(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, - fr_dbuff_t *in, fr_der_decode_ctx_t *decode_ctx) CC_HINT(nonnull); - -static ssize_t fr_der_decode_t61_string(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, - fr_dbuff_t *in, fr_der_decode_ctx_t *decode_ctx) CC_HINT(nonnull); - -static ssize_t fr_der_decode_ia5_string(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, - fr_dbuff_t *in, fr_der_decode_ctx_t *decode_ctx) CC_HINT(nonnull); - -static ssize_t fr_der_decode_utc_time(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, - fr_dbuff_t *in, fr_der_decode_ctx_t *decode_ctx) CC_HINT(nonnull); - -static ssize_t fr_der_decode_generalized_time(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, - fr_dbuff_t *in, fr_der_decode_ctx_t *decode_ctx) CC_HINT(nonnull); - -static ssize_t fr_der_decode_visible_string(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, - fr_dbuff_t *in, fr_der_decode_ctx_t *decode_ctx) CC_HINT(nonnull); - -static ssize_t fr_der_decode_general_string(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, - fr_dbuff_t *in, fr_der_decode_ctx_t *decode_ctx) CC_HINT(nonnull); - -static ssize_t fr_der_decode_universal_string(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, - fr_dbuff_t *in, fr_der_decode_ctx_t *decode_ctx) CC_HINT(nonnull); - -/* - * We have per-type function names to make it clear that different types have different decoders. - * However, the methods to decode them are the same. So rather than having trampoline functions, we just - * use defines. - */ -#define fr_der_decode_enumerated fr_der_decode_integer - -static 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 }, - [FR_DER_TAG_BITSTRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_bitstring }, - [FR_DER_TAG_OCTETSTRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_octetstring }, - [FR_DER_TAG_NULL] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_null }, - [FR_DER_TAG_ENUMERATED] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_enumerated }, - [FR_DER_TAG_UTF8_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_utf8_string }, - [FR_DER_TAG_SEQUENCE] = { .constructed = FR_DER_TAG_CONSTRUCTED, .decode = fr_der_decode_sequence }, - [FR_DER_TAG_SET] = { .constructed = FR_DER_TAG_CONSTRUCTED, .decode = fr_der_decode_set }, - [FR_DER_TAG_PRINTABLE_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, - .decode = fr_der_decode_printable_string }, - [FR_DER_TAG_T61_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_t61_string }, - [FR_DER_TAG_IA5_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_ia5_string }, - [FR_DER_TAG_UTC_TIME] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_utc_time }, - [FR_DER_TAG_GENERALIZED_TIME] = { .constructed = FR_DER_TAG_PRIMITIVE, - .decode = fr_der_decode_generalized_time }, - [FR_DER_TAG_VISIBLE_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_visible_string }, - [FR_DER_TAG_GENERAL_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_general_string }, - [FR_DER_TAG_UNIVERSAL_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, - .decode = fr_der_decode_universal_string }, -}; - static ssize_t fr_der_decode_string(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, fr_dbuff_t *in, bool const allowed_chars[], fr_der_decode_ctx_t *decode_ctx) CC_HINT(nonnull(1,2,3,4,6)); @@ -1504,6 +1426,13 @@ static ssize_t fr_der_decode_visible_string(TALLOC_CTX *ctx, fr_pair_list_t *out return fr_der_decode_string(ctx, out, parent, in, allowed_chars, decode_ctx); } +/* + * We have per-type function names to make it clear that different types have different decoders. + * However, the methods to decode them are the same. So rather than having trampoline functions, we just + * use defines. + */ +#define fr_der_decode_enumerated fr_der_decode_integer + static ssize_t fr_der_decode_general_string(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) { @@ -1516,6 +1445,102 @@ static ssize_t fr_der_decode_universal_string(TALLOC_CTX *ctx, fr_pair_list_t *o return fr_der_decode_string(ctx, out, parent, in, NULL, decode_ctx); } +static ssize_t fr_der_decode_ipv4_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) +{ + uint8_t byte; + fr_pair_t *vp; + fr_dbuff_t our_in = FR_DBUFF(in); + + 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)); + return -1; + } + + FR_DBUFF_OUT_RETURN(&byte, &our_in); + if (byte != 0) { + fr_strerror_printf("Invalid ipv4addr prefix is non-zero (%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 = 32; + FR_DBUFF_OUT_MEMCPY_RETURN((uint8_t *) &vp->vp_ipv4addr, &our_in, sizeof(vp->vp_ipv4addr)); + + 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) +{ + uint8_t byte; + fr_pair_t *vp; + fr_dbuff_t our_in = FR_DBUFF(in); + + 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)); + return -1; + } + + FR_DBUFF_OUT_RETURN(&byte, &our_in); + if (byte != 0) { + fr_strerror_printf("Invalid ipv6addr prefix is non-zero (%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 = 128; + FR_DBUFF_OUT_MEMCPY_RETURN((uint8_t *) &vp->vp_ipv6addr, &our_in, sizeof(vp->vp_ipv6addr)); + + 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 }, + [FR_DER_TAG_BITSTRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_bitstring }, + [FR_DER_TAG_OCTETSTRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_octetstring }, + [FR_DER_TAG_NULL] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_null }, + [FR_DER_TAG_ENUMERATED] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_enumerated }, + [FR_DER_TAG_UTF8_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_utf8_string }, + [FR_DER_TAG_SEQUENCE] = { .constructed = FR_DER_TAG_CONSTRUCTED, .decode = fr_der_decode_sequence }, + [FR_DER_TAG_SET] = { .constructed = FR_DER_TAG_CONSTRUCTED, .decode = fr_der_decode_set }, + [FR_DER_TAG_PRINTABLE_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, + .decode = fr_der_decode_printable_string }, + [FR_DER_TAG_T61_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_t61_string }, + [FR_DER_TAG_IA5_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_ia5_string }, + [FR_DER_TAG_UTC_TIME] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_utc_time }, + [FR_DER_TAG_GENERALIZED_TIME] = { .constructed = FR_DER_TAG_PRIMITIVE, + .decode = fr_der_decode_generalized_time }, + [FR_DER_TAG_VISIBLE_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_visible_string }, + [FR_DER_TAG_GENERAL_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_general_string }, + [FR_DER_TAG_UNIVERSAL_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, + .decode = fr_der_decode_universal_string }, +}; + +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_IPV6_ADDR] = { .constructed = FR_DER_TAG_PRIMITIVE, .decode = fr_der_decode_ipv6_addr }, +}; + /** Decode the tag and length fields of a DER encoded structure * * @param[in] parent Parent attribute @@ -1530,7 +1555,7 @@ static ssize_t fr_der_decode_hdr(fr_dict_attr_t const *parent, fr_dbuff_t *in, u fr_dbuff_t our_in = FR_DBUFF(in); uint8_t tag_byte; uint8_t len_byte; - fr_der_tag_decode_t *func; + fr_der_tag_decode_t const *func; fr_der_tag_class_t tag_class; fr_der_tag_constructed_t constructed; fr_der_attr_flags_t const *flags; @@ -1601,7 +1626,18 @@ static ssize_t fr_der_decode_hdr(fr_dict_attr_t const *parent, fr_dbuff_t *in, u return -1; } - func = &tag_funcs[*tag]; + 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]; + } /* * Check if the tag is an OID. OID tags will be handled differently @@ -2148,7 +2184,7 @@ static ssize_t fr_der_decode_pair_dbuff(TALLOC_CTX *ctx, fr_pair_list_t *out, fr fr_dbuff_t *in, fr_der_decode_ctx_t *decode_ctx) { fr_dbuff_t our_in = FR_DBUFF(in); - fr_der_tag_decode_t *func; + fr_der_tag_decode_t const *func; ssize_t slen; uint8_t tag; size_t len; @@ -2334,7 +2370,17 @@ static ssize_t fr_der_decode_pair_dbuff(TALLOC_CTX *ctx, fr_pair_list_t *out, fr return fr_dbuff_set(in, &our_in); } - func = &tag_funcs[tag]; + 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; + } + fr_assert(func != NULL); /* * Enforce limits on min/max. diff --git a/src/protocols/der/encode.c b/src/protocols/der/encode.c index b035040781d..e1a9befbd73 100644 --- a/src/protocols/der/encode.c +++ b/src/protocols/der/encode.c @@ -62,21 +62,9 @@ typedef struct { fr_der_encode_t encode; } fr_der_tag_encode_t; -static ssize_t fr_der_encode_boolean(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, fr_der_encode_ctx_t *encode_ctx) CC_HINT(nonnull(1,2)); -static ssize_t fr_der_encode_integer(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, fr_der_encode_ctx_t *encode_ctx) CC_HINT(nonnull(1,2)); -static ssize_t fr_der_encode_bitstring(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, fr_der_encode_ctx_t *encode_ctx) CC_HINT(nonnull); -static ssize_t fr_der_encode_octetstring(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, fr_der_encode_ctx_t *encode_ctx) CC_HINT(nonnull); -static ssize_t fr_der_encode_null(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, fr_der_encode_ctx_t *encode_ctx) CC_HINT(nonnull(2)); -static ssize_t fr_der_encode_oid(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, fr_der_encode_ctx_t *encode_ctx) CC_HINT(nonnull(1,2)); -static ssize_t fr_der_encode_sequence(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, fr_der_encode_ctx_t *encode_ctx) CC_HINT(nonnull); -static ssize_t fr_der_encode_set(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, fr_der_encode_ctx_t *encode_ctx) CC_HINT(nonnull); -static ssize_t fr_der_encode_utc_time(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, fr_der_encode_ctx_t *encode_ctx) CC_HINT(nonnull(1,2)); -static ssize_t fr_der_encode_generalized_time(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, fr_der_encode_ctx_t *encode_ctx) CC_HINT(nonnull(1,2)); static ssize_t fr_der_encode_oid_value_pair(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, fr_der_encode_ctx_t *encode_ctx) CC_HINT(nonnull); -static ssize_t fr_der_encode_string(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, fr_der_encode_ctx_t *encode_ctx) CC_HINT(nonnull(1,2)); - /* * We have per-type function names to make it clear that different types have different encoders. * However, the methods to encode them are the same. So rather than having trampoline functions, we just @@ -95,30 +83,6 @@ static ssize_t encode_pair(fr_dbuff_t *dbuff, fr_da_stack_t *da_stack, unsigned void *encode_ctx); static ssize_t der_encode_pair(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, void *encode_ctx) CC_HINT(nonnull); -static fr_der_tag_encode_t tag_funcs[FR_DER_TAG_MAX] = { - [FR_DER_TAG_BOOLEAN] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_boolean }, - [FR_DER_TAG_INTEGER] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_integer }, - [FR_DER_TAG_BITSTRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_bitstring }, - [FR_DER_TAG_OCTETSTRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_octetstring }, - [FR_DER_TAG_NULL] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_null }, - [FR_DER_TAG_OID] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_oid }, - [FR_DER_TAG_ENUMERATED] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_enumerated }, - [FR_DER_TAG_UTF8_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_string }, - [FR_DER_TAG_SEQUENCE] = { .constructed = FR_DER_TAG_CONSTRUCTED, .encode = fr_der_encode_sequence }, - [FR_DER_TAG_SET] = { .constructed = FR_DER_TAG_CONSTRUCTED, .encode = fr_der_encode_set }, - [FR_DER_TAG_PRINTABLE_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, - .encode = fr_der_encode_string }, - [FR_DER_TAG_T61_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_string }, - [FR_DER_TAG_IA5_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_string }, - [FR_DER_TAG_UTC_TIME] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_utc_time }, - [FR_DER_TAG_GENERALIZED_TIME] = { .constructed = FR_DER_TAG_PRIMITIVE, - .encode = fr_der_encode_generalized_time }, - [FR_DER_TAG_VISIBLE_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_string }, - [FR_DER_TAG_GENERAL_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_string }, - [FR_DER_TAG_UNIVERSAL_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, - .encode = fr_der_encode_string }, -}; - /** Compare two pairs by their tag number. * * @param[in] a First pair. @@ -381,7 +345,7 @@ static ssize_t fr_der_encode_bitstring(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, * because this information should be retained when encoding/decoding. */ if (vp->vp_length == 0) { - FR_DBUFF_IN_RETURN(&our_dbuff, 0x00); + FR_DBUFF_IN_RETURN(&our_dbuff, (uint8_t) 0x00); } else { FR_DBUFF_IN_MEMCPY_RETURN(&our_dbuff, vp->vp_octets, vp->vp_length); @@ -390,6 +354,72 @@ static ssize_t fr_der_encode_bitstring(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, return fr_dbuff_set(dbuff, &our_dbuff); } +static ssize_t fr_der_encode_ipv4_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); + fr_pair_t const *vp; + + 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.) + */ + + /* + * The number of unused bits in the last byte is always zero. + */ + FR_DBUFF_IN_RETURN(&our_dbuff, (uint8_t) 0x00); + FR_DBUFF_IN_MEMCPY_RETURN(&our_dbuff, (uint8_t const *) &vp->vp_ipv4addr, sizeof(vp->vp_ipv4addr)); + + 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); + fr_pair_t const *vp; + + 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.) + */ + + /* + * The number of unused bits in the last byte is always zero. + */ + FR_DBUFF_IN_RETURN(&our_dbuff, (uint8_t) 0x00); + FR_DBUFF_IN_MEMCPY_RETURN(&our_dbuff, (uint8_t const *) &vp->vp_ipv6addr, sizeof(vp->vp_ipv6addr)); + + return fr_dbuff_set(dbuff, &our_dbuff); +} + + + static ssize_t fr_der_encode_octetstring(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, UNUSED fr_der_encode_ctx_t *encode_ctx) { @@ -429,6 +459,7 @@ static ssize_t fr_der_encode_octetstring(fr_dbuff_t *dbuff, fr_dcursor_t *cursor return fr_dbuff_set(dbuff, &our_dbuff); } + static ssize_t fr_der_encode_null(UNUSED fr_dbuff_t *dbuff, fr_dcursor_t *cursor, UNUSED fr_der_encode_ctx_t *encode_ctx) { @@ -1519,6 +1550,36 @@ static ssize_t fr_der_encode_string(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, UNU return fr_dbuff_set(dbuff, &our_dbuff); } +static const fr_der_tag_encode_t tag_funcs[FR_DER_TAG_MAX] = { + [FR_DER_TAG_BOOLEAN] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_boolean }, + [FR_DER_TAG_INTEGER] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_integer }, + [FR_DER_TAG_BITSTRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_bitstring }, + [FR_DER_TAG_OCTETSTRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_octetstring }, + [FR_DER_TAG_NULL] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_null }, + [FR_DER_TAG_OID] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_oid }, + [FR_DER_TAG_ENUMERATED] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_enumerated }, + [FR_DER_TAG_UTF8_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_string }, + [FR_DER_TAG_SEQUENCE] = { .constructed = FR_DER_TAG_CONSTRUCTED, .encode = fr_der_encode_sequence }, + [FR_DER_TAG_SET] = { .constructed = FR_DER_TAG_CONSTRUCTED, .encode = fr_der_encode_set }, + [FR_DER_TAG_PRINTABLE_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, + .encode = fr_der_encode_string }, + [FR_DER_TAG_T61_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_string }, + [FR_DER_TAG_IA5_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_string }, + [FR_DER_TAG_UTC_TIME] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_utc_time }, + [FR_DER_TAG_GENERALIZED_TIME] = { .constructed = FR_DER_TAG_PRIMITIVE, + .encode = fr_der_encode_generalized_time }, + [FR_DER_TAG_VISIBLE_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_string }, + [FR_DER_TAG_GENERAL_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_string }, + [FR_DER_TAG_UNIVERSAL_STRING] = { .constructed = FR_DER_TAG_PRIMITIVE, + .encode = fr_der_encode_string }, +}; + +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_IPV6_ADDR] = { .constructed = FR_DER_TAG_PRIMITIVE, .encode = fr_der_encode_ipv6_addr }, +}; + + /** Encode the length field of a DER structure * * The input dbuff is composed of the following data: @@ -1646,7 +1707,7 @@ static ssize_t encode_value(fr_dbuff_t *dbuff, UNUSED fr_da_stack_t *da_stack, U fr_pair_t const *vp; fr_dbuff_t our_dbuff = FR_DBUFF(dbuff); fr_dbuff_marker_t marker, encoding_start; - fr_der_tag_encode_t *func; + fr_der_tag_encode_t const *func; fr_der_tag_t tag; fr_der_tag_class_t tag_class; fr_der_encode_ctx_t *uctx = encode_ctx; @@ -1749,11 +1810,17 @@ static ssize_t encode_value(fr_dbuff_t *dbuff, UNUSED fr_da_stack_t *da_stack, U fr_assert(tag < FR_DER_TAG_MAX); - func = &tag_funcs[tag]; - if (!func->encode) { - fr_strerror_printf("No encoding function for type %s", fr_type_to_str(vp->vp_type)); - return -1; + switch (vp->vp_type) { + case FR_TYPE_IPV4_ADDR: + case FR_TYPE_IPV6_ADDR: + func = &type_funcs[tag]; + break; + + default: + func = &tag_funcs[tag]; + break; } + fr_assert(func->encode != NULL); /* * Default flag class is 0, which is FR_DER_CLASS_UNIVERSAL. diff --git a/src/tests/unit/protocols/der/base.txt b/src/tests/unit/protocols/der/base.txt index d41fefed875..c7750ed338b 100644 --- a/src/tests/unit/protocols/der/base.txt +++ b/src/tests/unit/protocols/der/base.txt @@ -874,5 +874,15 @@ match 30 03 01 01 ff encode-pair Issuer = { RelativeDistinguishedName = { AttributeTypeAndValue = { joint-iso-itu-t = { ds = { attributeType = { organizationName = "Digital Signature Trust Co." } } } } }, RelativeDistinguishedName = { AttributeTypeAndValue = { joint-iso-itu-t = { ds = { attributeType = { commonName = "DST Root CA X3" } } } } } } match 30 3f 31 24 30 22 06 03 55 04 0a 13 1b 44 69 67 69 74 61 6c 20 53 69 67 6e 61 74 75 72 65 20 54 72 75 73 74 20 43 6f 2e 31 17 30 15 06 03 55 04 03 13 0e 44 53 54 20 52 6f 6f 74 20 43 41 20 58 33 +proto-dictionary der + +encode-pair Test-IPv4-Address = 10.5.0.4 +match 03 05 00 0a 05 00 04 + +proto-dictionary-root Test-IPv4-Address + +decode-pair 03 05 00 0a 05 00 04 +match Test-IPv4-Address = 10.5.0.4 + count -match 529 +match 535 diff --git a/src/tests/unit/protocols/der/dictionary.test b/src/tests/unit/protocols/der/dictionary.test index ff9c5d1114c..7ce33f51eee 100644 --- a/src/tests/unit/protocols/der/dictionary.test +++ b/src/tests/unit/protocols/der/dictionary.test @@ -30,6 +30,9 @@ DEFINE Test-Boolean bool DEFINE Test-Integer integer +DEFINE Test-IPv4-Address ipv4addr +DEFINE Test-IPv6-Address ipv4addr + DEFINE Foo sequence sequence_of=integer BEGIN Foo DEFINE Test-Integer integer