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.
*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;
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);
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.
* 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
* 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);
}
/*
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;
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...