From 0abefd8dcb09f6edd0f496121e45d5c464ad269d Mon Sep 17 00:00:00 2001 From: Arran Cudbard-Bell Date: Fri, 1 Dec 2017 15:20:38 +0000 Subject: [PATCH] Add support for encoding version lists --- share/dictionary.eap.sim | 2 +- src/modules/rlm_eap/lib/sim/base.c | 44 ++++++++++ src/modules/rlm_eap/lib/sim/decode.c | 2 +- src/modules/rlm_eap/lib/sim/encode.c | 104 +++++++++++++++++++++--- src/modules/rlm_eap/lib/sim/sim_proto.h | 3 + src/tests/unit/eap-sim.txt | 13 +++ 6 files changed, 156 insertions(+), 12 deletions(-) diff --git a/share/dictionary.eap.sim b/share/dictionary.eap.sim index 07e1d1e4731c..1242ae99010c 100644 --- a/share/dictionary.eap.sim +++ b/share/dictionary.eap.sim @@ -14,7 +14,7 @@ ATTRIBUTE EAP-SIM-Res 3 octets ATTRIBUTE EAP-SIM-Auts 4 octets ATTRIBUTE EAP-SIM-Nonce-MT 7 octets ATTRIBUTE EAP-SIM-Permanent-ID-Req 10 bool -ATTRIBUTE EAP-SIM-Mac 11 octets +ATTRIBUTE EAP-SIM-Mac 11 octets[20] ATTRIBUTE EAP-SIM-Notification 12 short VALUE EAP-SIM-Notification General-Failure-After-Authentication 0 diff --git a/src/modules/rlm_eap/lib/sim/base.c b/src/modules/rlm_eap/lib/sim/base.c index ad3955b7e9b9..dea540237b33 100644 --- a/src/modules/rlm_eap/lib/sim/base.c +++ b/src/modules/rlm_eap/lib/sim/base.c @@ -40,6 +40,50 @@ RCSID("$Id$") fr_dict_attr_t const *dict_sim_root; fr_dict_attr_t const *dict_aka_root; +/** SIM AT on-the-wire format attribute sizes + * + * Holds the min/max sizes of all supported SIM AT attribute values as they + * would be found in a SIM AT packet. + * + * These sizes may be different than the sizes of INTERNAL formats, PRESENTATION + * formats and generic NETWORK formats. + */ +size_t const fr_sim_attr_sizes[FR_TYPE_MAX + 1][2] = { + [FR_TYPE_INVALID] = {~0, 0}, + + [FR_TYPE_STRING] = {0, ~0}, + [FR_TYPE_OCTETS] = {0, ~0}, + + [FR_TYPE_BOOL] = {2, 2}, + [FR_TYPE_UINT8] = {1, 1}, + [FR_TYPE_UINT16] = {2, 2}, + [FR_TYPE_UINT32] = {4, 4}, + [FR_TYPE_UINT64] = {8, 8}, + + [FR_TYPE_TLV] = {2, ~0}, + + [FR_TYPE_MAX] = {~0, 0} //!< Ensure array covers all types. +}; + +/** Return the on-the-wire length of an attribute value + * + * @param[in] vp to return the length of. + * @return the length of the attribute. + */ +size_t fr_sim_attr_len(VALUE_PAIR const *vp) +{ + switch (vp->vp_type) { + case FR_TYPE_VARIABLE_SIZE: + return vp->vp_length; + + default: + return fr_radius_attr_sizes[vp->vp_type][0]; + + case FR_TYPE_STRUCTURAL: + if (!fr_cond_assert(0)) return 0; + } +} + /* * definitions changed to take a buffer for unknowns * as this is more thread safe. diff --git a/src/modules/rlm_eap/lib/sim/decode.c b/src/modules/rlm_eap/lib/sim/decode.c index 6d79d914501d..cf62cc201c56 100644 --- a/src/modules/rlm_eap/lib/sim/decode.c +++ b/src/modules/rlm_eap/lib/sim/decode.c @@ -256,7 +256,7 @@ static int fr_sim_array_members(size_t *out, size_t len, fr_dict_attr_t const *d break; default: - element_len = dict_attr_sizes[da->type][0]; + element_len = fr_sim_attr_sizes[da->type][0]; break; } diff --git a/src/modules/rlm_eap/lib/sim/encode.c b/src/modules/rlm_eap/lib/sim/encode.c index c660a29976b2..8b41c2f3b671 100644 --- a/src/modules/rlm_eap/lib/sim/encode.c +++ b/src/modules/rlm_eap/lib/sim/encode.c @@ -338,6 +338,13 @@ static ssize_t encode_value(uint8_t *out, size_t outlen, *p++ = 0; /* Reserved */ *p++ = 0; /* Reserved */ + if (vp->da->flags.length && (vp->vp_length != vp->da->flags.length)) { + fr_strerror_printf("%s: Attribute \"%s\" needs a value of exactly %zu bytes, " + "but value was %zu bytes", __FUNCTION__, + vp->da->name, (size_t)vp->da->flags.length, vp->vp_length); + return -1; + } + memcpy(p, vp->vp_octets, vp->vp_length); p += vp->vp_length; @@ -376,6 +383,13 @@ static ssize_t encode_value(uint8_t *out, size_t outlen, if ((rounded_len + 2) > outlen) goto oos; + if (vp->da->flags.length && (vp->vp_length != vp->da->flags.length)) { + fr_strerror_printf("%s: Attribute \"%s\" needs a value of exactly %zu bytes, " + "but value was %zu bytes", __FUNCTION__, + vp->da->name, (size_t)vp->da->flags.length, vp->vp_length); + return -1; + } + memcpy(p, &actual_len, sizeof(actual_len)); /* Big endian real string length */ p += sizeof(actual_len); @@ -444,6 +458,71 @@ static ssize_t encode_value(uint8_t *out, size_t outlen, return len; } +/** Encodes the data portion of an attribute + * + @verbatim + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | AT_VERSION_L..| Length | Actual Version List Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Supported Version 1 | Supported Version 2 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + . . + . . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Supported Version N | Padding | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + @endverbatim + * + */ +static ssize_t encode_array(uint8_t *out, size_t outlen, + fr_dict_attr_t const **tlv_stack, int depth, + vp_cursor_t *cursor, void *encoder_ctx) +{ + uint8_t *p = out, *end = p + outlen; + uint8_t *value; + size_t pad_len; + uint16_t actual_len; + fr_dict_attr_t const *da = tlv_stack[depth]; + + rad_assert(da->flags.array); + + p += 2; + value = p; /* Space for actual length */ + + /* + * Keep encoding as long as we have space to + * encode things. + */ + while (fr_sim_attr_sizes[da->type][0] <= ((size_t)(end - p))) { + VALUE_PAIR *vp; + ssize_t slen; + + slen = encode_value(p, end - p, tlv_stack, depth, cursor, encoder_ctx); + if (slen < 0) return slen; + + p += slen; + + vp = fr_pair_cursor_current(cursor); + if (!vp || (vp->da != da)) break; /* Stop if we have an attribute of a different type */ + } + + actual_len = htons((p - value) & UINT16_MAX); /* Length of the elements we encoded */ + memcpy(out, &actual_len, sizeof(actual_len)); + + /* + * Pad value a multiple of 4 + */ + pad_len = (((p - value) + 3) & ~3) - (p - value); + if (pad_len) { + memset(p, 0, pad_len); + p += pad_len; + } + + return p - out; +} + /** Encode an RFC format attribute header * * This could be a standard attribute, or a TLV data type. @@ -487,9 +566,13 @@ static ssize_t encode_rfc_hdr(uint8_t *out, size_t outlen, fr_dict_attr_t const * length. */ da = tlv_stack[depth]; - slen = encode_value(out + 2, outlen - 2, tlv_stack, depth, cursor, encoder_ctx); - if (slen <= 0) return slen; + if (da->flags.array) { + slen = encode_array(out + 2, outlen - 2, tlv_stack, depth, cursor, encoder_ctx); + } else { + slen = encode_value(out + 2, outlen - 2, tlv_stack, depth, cursor, encoder_ctx); + } + if (slen <= 0) return slen; /* * Round attr + len + data length out to a multiple * of four, and setup the attribute header and @@ -668,10 +751,11 @@ ssize_t fr_sim_encode_pair(uint8_t *out, size_t outlen, vp_cursor_t *cursor, voi * Fast path for the common case. */ if ((vp->da->parent == packet_ctx->root) && !vp->da->flags.concat && (vp->vp_type != FR_TYPE_TLV)) { - tlv_stack[0] = vp->da; - tlv_stack[1] = NULL; + tlv_stack[0] = packet_ctx->root; + tlv_stack[1] = vp->da; + tlv_stack[2] = NULL; FR_PROTO_STACK_PRINT(tlv_stack, 0); - return encode_rfc_hdr(out, attr_len, tlv_stack, 0, cursor, encoder_ctx); + return encode_rfc_hdr(out, attr_len, tlv_stack, 1, cursor, encoder_ctx); } /* @@ -772,8 +856,8 @@ ssize_t fr_sim_encode(REQUEST *request, fr_dict_attr_t const *parent, uint8_t ty MEM(buff = talloc_array(eap_packet, uint8_t, 3)); buff[0] = subtype; /* SIM or AKA subtype */ - buff[1] = 0; /* Reserved */ - buff[2] = 0; /* Reserved */ + buff[1] = 0; /* Reserved (0) */ + buff[2] = 0; /* Reserved (1) */ eap_packet->type.length = 3; eap_packet->type.data = buff; @@ -786,9 +870,9 @@ ssize_t fr_sim_encode(REQUEST *request, fr_dict_attr_t const *parent, uint8_t ty end = p + talloc_array_length(p); if (do_hmac) end -= SIM_CALC_MAC_SIZE; - *p++ = subtype; /* Subtype (1) */ - *p++ = 0; /* Reserved */ - *p++ = 0; /* Reserved */ + *p++ = subtype; /* Subtype */ + *p++ = 0; /* Reserved (0) */ + *p++ = 0; /* Reserved (1) */ /* * Encode all the things... diff --git a/src/modules/rlm_eap/lib/sim/sim_proto.h b/src/modules/rlm_eap/lib/sim/sim_proto.h index 3dd98d6b4173..0adf90e69362 100644 --- a/src/modules/rlm_eap/lib/sim/sim_proto.h +++ b/src/modules/rlm_eap/lib/sim/sim_proto.h @@ -163,6 +163,7 @@ typedef struct { typedef struct _eap_session eap_session_t; +extern size_t const fr_sim_attr_sizes[FR_TYPE_MAX + 1][2]; extern fr_dict_attr_t const *dict_sim_root; extern fr_dict_attr_t const *dict_aka_root; @@ -186,6 +187,8 @@ ssize_t fr_sim_encode(REQUEST *request, fr_dict_attr_t const *parent, uint8_t t /* * base.c */ +size_t fr_sim_attr_len(VALUE_PAIR const *vp); + char const *fr_sim_session_to_name(char *out, size_t outlen, eap_sim_client_states_t state); int fr_sim_global_init(void); diff --git a/src/tests/unit/eap-sim.txt b/src/tests/unit/eap-sim.txt index 4e0d16dd7086..28e3fdffb89a 100644 --- a/src/tests/unit/eap-sim.txt +++ b/src/tests/unit/eap-sim.txt @@ -78,3 +78,16 @@ data 82 05 00 00 3f b8 34 1f f8 26 e0 4d 4a f3 f9 61 3c a9 84 26 0d 01 00 00 82 decode-pair.tp_decode_sim - data EAP-SIM-Next-Pseudonym = "testing123", EAP-SIM-Any-ID-Req = yes, EAP-SIM-Counter-Too-Small = yes + +# Array (one element) +encode-pair.tp_encode_sim EAP-SIM-Version-List = 1 +data 0f 02 00 02 00 01 00 00 + +# Array (multiple elements, with padding) +encode-pair.tp_encode_sim EAP-SIM-Version-List = 1, EAP-SIM-Version-List = 2, EAP-SIM-Version-List = 3 +data 0f 03 00 06 00 01 00 02 00 03 00 00 + +# Array (multiple elements, with no padding) +encode-pair.tp_encode_sim EAP-SIM-Version-List = 1, EAP-SIM-Version-List = 2, EAP-SIM-Version-List = 3, EAP-SIM-Version-List = 4 +data 0f 03 00 08 00 01 00 02 00 03 00 04 +