Esempio n. 1
0
static ssize_t decode_value(TALLOC_CTX *ctx, fr_cursor_t *cursor,
			    fr_dict_attr_t const *parent, uint8_t const *data, size_t data_len)
{
	unsigned int	values, i;		/* How many values we need to decode */
	uint8_t const	*p = data;
	size_t		value_len;
	ssize_t		len;

	FR_PROTO_TRACE("%s called to parse %zu byte(s)", __FUNCTION__, data_len);
	FR_PROTO_HEX_DUMP(data, data_len, NULL);

	/*
	 *	TLVs can't be coalesced as they're variable length
	 */
	if (parent->type == FR_TYPE_TLV) return decode_tlv(ctx, cursor, parent, data, data_len);

	/*
	 *	Values with a fixed length may be coalesced into a single option
	 */
	values = fr_dhcpv4_array_members(&value_len, data_len, parent);
	if (values) {
		FR_PROTO_TRACE("found %u coalesced values (%zu bytes each)", values, value_len);

		if ((values * value_len) != data_len) {
			fr_strerror_printf("Option length not divisible by its fixed value "
					  "length (probably trailing garbage)");
			return -1;
		}
	}

	/*
	 *	Decode each of the (maybe) coalesced values as its own
	 *	attribute.
	 */
	for (i = 0, p = data; i < values; i++) {
		len = decode_value_internal(ctx, cursor, parent, p, value_len);
		if (len <= 0) return len;
		if (len != (ssize_t)value_len) {
			fr_strerror_printf("Failed decoding complete option value");
			return -1;
		}
		p += len;
	}

	return p - data;
}
Esempio n. 2
0
static ssize_t sim_decode_array(TALLOC_CTX *ctx, fr_cursor_t *cursor,
				fr_dict_attr_t const *parent,
				uint8_t const *data, size_t const attr_len, UNUSED size_t data_len,
				void *decoder_ctx)
{
	uint8_t const	*p = data, *end = p + attr_len;
	uint16_t	actual_len;
	int		elements, i;
	size_t		element_len;
	ssize_t		rcode;

	FR_PROTO_TRACE("Array attribute");

	rad_assert(parent->flags.array);
	rad_assert(attr_len >= 2);		/* Should have been caught earlier */

	/*
	 *	Arrays with fixed length members that
	 *	are a multiple of 4 don't need an
	 *	actual_len value, as we can get the
	 *	number of elements from the attribute
	 *	length.
	 */
	if (!parent->flags.length || (parent->flags.length % 4)) {
		actual_len = (p[0] << 8) | p[1];
		if (actual_len > (attr_len - 2)) {
			fr_strerror_printf("%s: Actual length field value (%hu) > attribute value length (%zu)",
					   __FUNCTION__, actual_len, attr_len - 2);
			return -1;
		}
	} else {
		actual_len = attr_len - 2;	/* -2 for the reserved bytes */
	}
	p += 2;

	/*
	 *	Zero length array
	 */
	if (!actual_len) return p - data;

	/*
	 *	Get the number of elements
	 */
	elements = sim_array_members(&element_len, actual_len, parent);
	if (elements < 0) return elements;

	for (i = 0; i < elements; i++) {
		rcode = sim_decode_pair_value(ctx, cursor, parent, p, element_len, end - p, decoder_ctx);
		if (rcode < 0) return rcode;

		p += rcode;

		if (!fr_cond_assert(p <= end)) break;
	}

	return attr_len;	/* Say we consumed attr_len because it may have padding */
}
Esempio n. 3
0
/*
 *	Decode ONE value into a VP
 */
static ssize_t decode_value_internal(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_attr_t const *da,
				     uint8_t const *data, size_t data_len)
{
	VALUE_PAIR *vp;
	uint8_t const *p = data;

	FR_PROTO_TRACE("%s called to parse %zu bytes", __FUNCTION__, data_len);
	FR_PROTO_HEX_DUMP(data, data_len, NULL);

	vp = fr_pair_afrom_da(ctx, da);
	if (!vp) return -1;

	/*
	 *	Unknown attributes always get converted to
	 *	octet types, so there's no way there could
	 *	be multiple attributes, so its safe to
	 *	steal the unknown attribute into the context
	 *	of the pair.
	 */
	if (da->flags.is_unknown) talloc_steal(vp, da);

	if (vp->da->type == FR_TYPE_STRING) {
		uint8_t const *q, *end;

		q = end = data + data_len;

		/*
		 *	Not allowed to be an array, copy the whole value
		 */
		if (!vp->da->flags.array) {
			fr_pair_value_bstrncpy(vp, (char const *)p, end - p);
			p = end;
			goto finish;
		}

		for (;;) {
			q = memchr(p, '\0', q - p);

			/* Malformed but recoverable */
			if (!q) q = end;

			fr_pair_value_bstrncpy(vp, (char const *)p, q - p);
			p = q + 1;
			vp->vp_tainted = true;

			/* Need another VP for the next round */
			if (p < end) {
				fr_cursor_append(cursor, vp);

				vp = fr_pair_afrom_da(ctx, da);
				if (!vp) return -1;
				continue;
			}
			break;
		}
		goto finish;
	}

	switch (vp->da->type) {
	/*
	 *	Doesn't include scope, whereas the generic format can
	 */
	case FR_TYPE_IPV6_ADDR:
		memcpy(&vp->vp_ipv6addr, p, sizeof(vp->vp_ipv6addr));
		vp->vp_ip.af = AF_INET6;
		vp->vp_ip.scope_id = 0;
		vp->vp_ip.prefix = 128;
		vp->vp_tainted = true;
		p += sizeof(vp->vp_ipv6addr);
		break;

	case FR_TYPE_IPV6_PREFIX:
		memcpy(&vp->vp_ipv6addr, p + 1, sizeof(vp->vp_ipv6addr));
		vp->vp_ip.af = AF_INET6;
		vp->vp_ip.scope_id = 0;
		vp->vp_ip.prefix = p[0];
		vp->vp_tainted = true;
		p += sizeof(vp->vp_ipv6addr) + 1;
		break;

	default:
	{
		ssize_t ret;

		ret = fr_value_box_from_network(vp, &vp->data, vp->da->type, da, p, data_len, true);
		if (ret < 0) {
			FR_PROTO_TRACE("decoding as unknown type");
			if (fr_pair_to_unknown(vp) < 0) return -1;
			fr_pair_value_memcpy(vp, p, data_len);
			ret = data_len;
		}
		p += (size_t) ret;
	}
	}

finish:
	FR_PROTO_TRACE("decoding value complete, adding new pair and returning %zu byte(s)", p - data);
	fr_cursor_append(cursor, vp);

	return p - data;
}
Esempio n. 4
0
/** Decode DHCP option
 *
 * @param[in] ctx context	to alloc new attributes in.
 * @param[in,out] cursor	Where to write the decoded options.
 * @param[in] dict		to lookup attributes in.
 * @param[in] data		to parse.
 * @param[in] data_len		of data to parse.
 * @param[in] decoder_ctx	Unused.
 */
ssize_t fr_dhcpv4_decode_option(TALLOC_CTX *ctx, fr_cursor_t *cursor,
			        fr_dict_t const *dict, uint8_t const *data, size_t data_len, UNUSED void *decoder_ctx)
{
	ssize_t			ret;
	uint8_t const		*p = data;
	fr_dict_attr_t const	*child;
	fr_dict_attr_t const	*parent;

	FR_PROTO_TRACE("%s called to parse %zu byte(s)", __FUNCTION__, data_len);

	if (data_len == 0) return 0;

	FR_PROTO_HEX_DUMP(data, data_len, NULL);

	parent = fr_dict_root(dict);

	/*
	 *	Padding / End of options
	 */
	if (p[0] == 0) return 1;		/* 0x00 - Padding option */
	if (p[0] == 255) {			/* 0xff - End of options signifier */
		size_t i;

		for (i = 1; i < data_len; i++) {
			if (p[i] != 0) {
				FR_PROTO_HEX_DUMP(p + i, data_len - i, "ignoring trailing junk at end of packet");
				break;
			}
		}
		return data_len;
	}

	/*
	 *	Everything else should be real options
	 */
	if ((data_len < 2) || (data[1] > data_len)) {
		fr_strerror_printf("%s: Insufficient data", __FUNCTION__);
		return -1;
	}

	child = fr_dict_attr_child_by_num(parent, p[0]);
	if (!child) {
		/*
		 *	Unknown attribute, create an octets type
		 *	attribute with the contents of the sub-option.
		 */
		child = fr_dict_unknown_afrom_fields(ctx, parent, fr_dict_vendor_num_by_da(parent), p[0]);
		if (!child) return -1;
	}
	FR_PROTO_TRACE("decode context changed %s:%s -> %s:%s",
		       fr_int2str(fr_value_box_type_table, parent->type, "<invalid>"), parent->name,
		       fr_int2str(fr_value_box_type_table, child->type, "<invalid>"), child->name);

	ret = decode_value(ctx, cursor, child, data + 2, data[1]);
	if (ret < 0) {
		fr_dict_unknown_free(&child);
		return ret;
	}
	ret += 2; /* For header */
	FR_PROTO_TRACE("decoding option complete, returning %zu byte(s)", ret);
	return ret;
}
Esempio n. 5
0
/** Decode DHCP suboptions
 *
 * @param[in] ctx context to alloc new attributes in.
 * @param[in,out] cursor Where to write the decoded options.
 * @param[in] parent of sub TLVs.
 * @param[in] data to parse.
 * @param[in] data_len of data parsed.
 */
static ssize_t decode_tlv(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_attr_t const *parent,
			  uint8_t const *data, size_t data_len)
{
	uint8_t const		*p = data;
	uint8_t const		*end = data + data_len;
	fr_dict_attr_t const	*child;

	if (data_len < 3) return -1; /* type, length, value */

	FR_PROTO_TRACE("%s called to parse %zu byte(s)", __FUNCTION__, data_len);
	FR_PROTO_HEX_DUMP(data, data_len, NULL);

	/*
	 *	Each TLV may contain multiple children
	 */
	while (p < end) {
		ssize_t tlv_len;

		if (p[0] == 0) {
			p++;
			continue;
		}

		/*
		 *	RFC 3046 is very specific about not allowing termination
		 *	with a 255 sub-option. But it's required for decoding
		 *	option 43, and vendors will probably screw it up
		 *	anyway.
		 */
		if (p[0] == 255) {
			p++;
			return p - data;
		}

		/*
		 *	Everything else should be real options
		 */
		if ((end - p) < 2) {
			fr_strerror_printf("%s: Insufficient data: Needed at least 2 bytes, got %zu",
					   __FUNCTION__, (end - p));
			return -1;
		}

		if (p[1] > (end - p)) {
			fr_strerror_printf("%s: Suboption would overflow option.  Remaining option data %zu byte(s) "
					   "(from %zu), Suboption length %u", __FUNCTION__, (end - p), data_len, p[1]);
			return -1;
		}

		child = fr_dict_attr_child_by_num(parent, p[0]);
		if (!child) {
			fr_dict_attr_t const *unknown_child;

			FR_PROTO_TRACE("failed to find child %u of TLV %s", p[0], parent->name);

			/*
			 *	Build an unknown attr
			 */
			unknown_child = fr_dict_unknown_afrom_fields(ctx, parent,
								     fr_dict_vendor_num_by_da(parent), p[0]);
			if (!unknown_child) return -1;
			child = unknown_child;
		}
		FR_PROTO_TRACE("decode context changed %s:%s -> %s:%s",
			       fr_int2str(fr_value_box_type_table, parent->type, "<invalid>"), parent->name,
			       fr_int2str(fr_value_box_type_table, child->type, "<invalid>"), child->name);

		tlv_len = decode_value(ctx, cursor, child, p + 2, p[1]);
		if (tlv_len < 0) {
			fr_dict_unknown_free(&child);
			return tlv_len;
		}
		p += tlv_len + 2;
		FR_PROTO_TRACE("decode_value returned %zu, adding 2 (for header)", tlv_len);
		FR_PROTO_TRACE("remaining TLV data %zu byte(s)" , end - p);
	}
	FR_PROTO_TRACE("tlv parsing complete, returning %zu byte(s)", p - data);

	return p - data;
}
Esempio n. 6
0
/** Decode SIM/AKA/AKA' attributes
 *
 * @param[in] ctx		to allocate attributes in.
 * @param[in] cursor		where to insert the attributes.
 * @param[in] parent		of current attribute being decoded.
 * @param[in] data		data to parse.
 * @param[in] data_len		length of data.  For top level attributes packet_ctx must be the length
 *				of the packet (so we can hunt for AT_IV), for Sub-TLVs it should
 *				be the length of the container value.
 * @param[in] decoder_ctx	extra context to pass to the decoder.
 * @return
 *	- The number of bytes parsed.
 *	- -1 on error.
 */
static ssize_t sim_decode_pair_internal(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_attr_t const *parent,
					uint8_t const *data, size_t data_len, void *decoder_ctx)
{
	uint8_t			sim_at;
	size_t			sim_at_len;

	ssize_t			rcode;
	fr_dict_attr_t const	*da;


	/*
	 *	We need at least 2 bytes.  We really need 4 but it's
	 *	useful to print the attribute number in the errors.
	 */
	if (data_len < 2) {
		fr_strerror_printf("%s: Insufficient data: Expected >= 2 bytes, got %zu bytes",
				   __FUNCTION__, data_len);
		return -1;
	}

	sim_at = data[0];

	sim_at_len = ((size_t)data[1]) << 2;
	if (sim_at_len > data_len) {
		fr_strerror_printf("%s: Insufficient data for attribute %d: Length field %zu, remaining data %zu",
				   __FUNCTION__, sim_at, sim_at_len, data_len);
		return -1;
	}

	if (sim_at_len == 0) {
		fr_strerror_printf("%s: Malformed attribute %d: Length field 0", __FUNCTION__, sim_at);
		return -1;
	}

	da = fr_dict_attr_child_by_num(parent, sim_at);
	if (!da) {
		FR_PROTO_TRACE("Unknown attribute %u", sim_at);

		/*
		 *	Encountered none skippable attribute
		 *
		 *	RFC says we need to die on these if we don't
		 *	understand them.  non-skippables are < 128.
		 */
		if (sim_at <= SIM_SKIPPABLE_MAX) {
			fr_strerror_printf("Unknown (non-skippable) attribute %i", sim_at);
			return -1;
		}
		da = fr_dict_unknown_afrom_fields(ctx, parent, 0, sim_at);
	}
	if (!da) return -1;

	FR_PROTO_TRACE("decode context changed %s -> %s", da->parent->name, da->name);

	if (da->flags.array) {
		rcode = sim_decode_array(ctx, cursor, da, data + 2, sim_at_len - 2, data_len - 2, decoder_ctx);
	} else {
		rcode = sim_decode_pair_value(ctx, cursor, da, data + 2, sim_at_len - 2, data_len - 2, decoder_ctx);
	}
	if (rcode < 0) return rcode;

	return 2 + rcode;
}
Esempio n. 7
0
/** Create any kind of VP from the attribute contents
 *
 * @param[in] ctx		to allocate new attributes in.
 * @param[in] cursor		to addd new attributes to.
 * @param[in] parent		the current attribute we're processing.
 * @param[in] data		to parse. Points to the data field of the attribute.
 * @param[in] attr_len		length of the attribute being parsed.
 * @param[in] data_len		length of the remaining data in the packet.
 * @param[in] decoder_ctx	IVs, keys etc...
 * @return
 *	- Length on success.
 *	- -1 on failure.
 */
static ssize_t sim_decode_pair_value(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_attr_t const *parent,
				     uint8_t const *data, size_t const attr_len, size_t const data_len,
				     void *decoder_ctx)
{
	VALUE_PAIR		*vp;
	uint8_t const		*p = data;
	size_t			prefix = 0;

	fr_sim_decode_ctx_t	*packet_ctx = decoder_ctx;

	if (!fr_cond_assert(attr_len <= data_len)) return -1;
	if (!fr_cond_assert(parent)) return -1;

	FR_PROTO_TRACE("Parent %s len %zu", parent->name, attr_len);
	FR_PROTO_HEX_DUMP(data, attr_len, __FUNCTION__ );

	FR_PROTO_TRACE("Type \"%s\" (%u)", fr_int2str(fr_value_box_type_table, parent->type, "?Unknown?"), parent->type);

	/*
	 *	Special cases, attributes that either have odd formats, or need
	 *	have information we need to decode the packet.
	 */
	switch (parent->attr) {
	/*
	 *	We need to record packet_ctx so we can decrypt AT_ENCR attributes.
	 *
	 *	If we don't find it before, then that's fine, we'll try and
	 *	find it in the rest of the packet after the encrypted
	 *	attribute.
	 *
	 *	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_IV     | Length = 5    |           Reserved            |
	 *	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *	|                                                               |
	 *	|                 Initialization Vector                         |
	 *	|                                                               |
	 *	|                                                               |
	 *	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 */
	case FR_SIM_IV:
		if (sim_iv_extract(&packet_ctx->iv[0], data, attr_len) < 0) return -1;
		packet_ctx->have_iv = true;
		break;	/* Now create the attribute */

	/*
	 *	AT_RES - Special case (RES length is in bits)
	 *
	 *	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_RES    |    Length     |          RES Length           |
	 *	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
	 *	|                                                               |
	 *	|                             RES                               |
	 *	|                                                               |
	 *	|                                                               |
	 *	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 */
	case FR_EAP_AKA_RES:
	{
		uint16_t res_len;

		if (attr_len < 2) goto raw;	/* Need at least two bytes for the length field */

		res_len = (p[0] << 8) | p[1];
		if (res_len % 8) {
			fr_strerror_printf("%s: RES Length (%hu) is not a multiple of 8",
					   __FUNCTION__, res_len);
			return -1;
		}
		res_len /= 8;

		if (res_len > (attr_len - 2)) {
			fr_strerror_printf("%s: RES Length field value (%u bits) > attribute value length (%zu bits)",
					   __FUNCTION__, res_len * 8, (attr_len - 2) * 8);
			return -1;
		}

		if ((res_len < 4) || (res_len > 16)) {
			fr_strerror_printf("%s: RES Length field value must be between 32-128 bits, got %u bits",
					   __FUNCTION__, (res_len * 8));
			return -1;
		}

		vp = fr_pair_afrom_da(ctx, parent);
		if (!vp) return -1;

		fr_pair_value_memcpy(vp, p + 2, res_len, true);
	}
		goto done;

	/*
	 *	AT_CHECKCODE - Special case (Variable length with no length field)
	 *
	 *   	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_CHECKCODE  | Length        |           Reserved            |
	 *	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *	|                                                               |
	 *	|                     Checkcode (0 or 20 bytes)                 |
	 *	|                                                               |
	 *	|                                                               |
	 *	|                                                               |
	 *	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 */
	case FR_EAP_AKA_CHECKCODE:
		if (attr_len < 2) goto raw;	/* Need at least two bytes for reserved field */

		vp = fr_pair_afrom_da(ctx, parent);
		if (!vp) return -1;

		fr_pair_value_memcpy(vp, p + 2, attr_len - 2, true);
		goto done;

	default:
		break;
	}

	switch (parent->type) {
	case FR_TYPE_STRING:
		if (attr_len < 2) goto raw;	/* Need at least two bytes for the length field */
		if (parent->flags.length && (attr_len != parent->flags.length)) {
		wrong_len:
			fr_strerror_printf("%s: Attribute \"%s\" needs a value of exactly %zu bytes, "
					   "but value was %zu bytes", __FUNCTION__,
					   parent->name, (size_t)parent->flags.length, attr_len);
			goto raw;
		}
		break;

	case FR_TYPE_OCTETS:
		/*
		 *	Get the number of bytes we expect before the value
		 */
		prefix = fr_sim_octets_prefix_len(parent);
		if (attr_len < prefix) goto raw;
		if (parent->flags.length && (attr_len != (parent->flags.length + prefix))) goto wrong_len;
		break;

	case FR_TYPE_BOOL:
	case FR_TYPE_UINT8:
	case FR_TYPE_UINT16:
	case FR_TYPE_UINT32:
	case FR_TYPE_UINT64:
		if (attr_len != fr_sim_attr_sizes[parent->type][0]) goto raw;
		break;

	case FR_TYPE_TLV:
		if (attr_len < 2) goto raw;

		/*
		 *	We presume that the TLVs all fit into one
		 *	attribute, OR they've already been grouped
		 *	into a contiguous memory buffer.
		 */
		return sim_decode_tlv(ctx, cursor, parent, p, attr_len, data_len, decoder_ctx);

	default:
	raw:
		/*
		 *	We can't create unknowns for non-skippable attributes
		 *	as we're prohibited from continuing by the SIM RFCs.
		 */
		if (parent->attr <= SIM_SKIPPABLE_MAX) {
			fr_strerror_printf_push("%s: Failed parsing non-skippable attribute '%s'",
						__FUNCTION__, parent->name);
			return -1;
		}

#ifdef __clang_analyzer__
		if (!parent->parent) return -1; /* stupid static analyzers */
#endif
		rad_assert(parent->parent);

		/*
		 *	Re-write the attribute to be "raw".  It is
		 *	therefore of type "octets", and will be
		 *	handled below.
		 */
		parent = fr_dict_unknown_afrom_fields(ctx, parent->parent,
						      fr_dict_vendor_num_by_da(parent), parent->attr);
		if (!parent) {
			fr_strerror_printf_push("%s[%d]: Internal sanity check failed", __FUNCTION__, __LINE__);
			return -1;
		}
	}

	vp = fr_pair_afrom_da(ctx, parent);
	if (!vp) return -1;

	/*
	 *	For unknown attributes copy the entire value, not skipping
	 *	any reserved bytes.
	 */
	if (parent->flags.is_unknown || parent->flags.is_raw) {
		fr_pair_value_memcpy(vp, p, attr_len, true);
		vp->vp_length = attr_len;
		goto done;
	}

	switch (parent->type) {
	/*
	 *	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_<STRING>   | Length        |    Actual <STRING> Length     |
	 *	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *	|                                                               |
	 *	.                           String                              .
	 *	.                                                               .
	 *	|                                                               |
	 *	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 */
	case FR_TYPE_STRING:
	{
		uint16_t actual_len = (p[0] << 8) | p[1];

		if (actual_len > (attr_len - 2)) {
			fr_strerror_printf("%s: Actual length field value (%hu) > attribute value length (%zu)",
					   __FUNCTION__, actual_len, attr_len - 2);
			return -1;
		}

		fr_pair_value_bstrncpy(vp, p + 2, actual_len);
	}
		break;

	case FR_TYPE_OCTETS:
		/*
		 *	Variable length octets buffer
		 */
		if (!parent->flags.length) {
			uint16_t actual_len = (p[0] << 8) | p[1];

			if (actual_len > (attr_len - prefix)) {
				fr_strerror_printf("%s: Actual length field value (%hu) > attribute value length (%zu)",
						   __FUNCTION__, actual_len, attr_len - 2);
				return -1;
			}

			fr_pair_value_memcpy(vp, p + prefix, actual_len, true);
		/*
		 *	Fixed length octets buffer
		 */
		} else {
			fr_pair_value_memcpy(vp, p + prefix, attr_len - prefix, true);
		}
		break;

	/*
	 *	Not proper bool. We Use packet_ctx to represent
	 *	flag attributes like AT_FULLAUTH_ID_REQ
	 *
	 *	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_<BOOL>   | Length = 1    |           Reserved            |
	 *	+---------------+---------------+-------------------------------+
	 */
	case FR_TYPE_BOOL:
		vp->vp_bool = true;
		break;

	/*
	 *	Numbers are network byte order.
	 *
	 *	In the base RFCs only short (16bit) unsigned integers are used.
	 *	We add support for more, just for completeness.
	 *
	 *	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_<SHORT>  | Length = 1    |    Short 1    |    Short 2    |
	 *	+---------------+---------------+-------------------------------+
	 */
	case FR_TYPE_UINT8:
		vp->vp_uint8 = p[0];
		break;

	case FR_TYPE_UINT16:
		memcpy(&vp->vp_uint16, p, sizeof(vp->vp_uint16));
		vp->vp_uint16 = ntohs(vp->vp_uint32);
		break;

	case FR_TYPE_UINT32:
		memcpy(&vp->vp_uint32, p, sizeof(vp->vp_uint32));
		vp->vp_uint32 = ntohl(vp->vp_uint32);
		break;

	case FR_TYPE_UINT64:
		memcpy(&vp->vp_uint64, p, sizeof(vp->vp_uint64));
		vp->vp_uint64 = ntohll(vp->vp_uint64);
		break;

	default:
		fr_pair_list_free(&vp);
		fr_strerror_printf_push("%s[%d]: Internal sanity check failed", __FUNCTION__, __LINE__);
		return -1;
	}

done:
	vp->type = VT_DATA;
	fr_cursor_append(cursor, vp);

	return attr_len;
}
Esempio n. 8
0
/** Break apart a TLV attribute into individual attributes
 *
 * @param[in] ctx		to allocate new attributes in.
 * @param[in] cursor		to addd new attributes to.
 * @param[in] parent		the current attribute TLV attribute we're processing.
 * @param[in] data		to parse. Points to the data field of the attribute.
 * @param[in] attr_len		length of the TLV attribute.
 * @param[in] data_len		remaining data in the packet.
 * @param[in] decoder_ctx	IVs, keys etc...
 * @return
 *	- Length on success.
 *	- < 0 on malformed attribute.
 */
static ssize_t sim_decode_tlv(TALLOC_CTX *ctx, fr_cursor_t *cursor,
			      fr_dict_attr_t const *parent,
			      uint8_t const *data, size_t const attr_len, size_t data_len,
			      void *decoder_ctx)
{
	uint8_t const		*p = data, *end = p + attr_len;
	uint8_t			*decr = NULL;
	ssize_t			decr_len;
	fr_dict_attr_t const	*child;
	VALUE_PAIR		*head = NULL;
	fr_cursor_t		tlv_cursor;
	ssize_t			rcode;

	if (data_len < 2) {
		fr_strerror_printf("%s: Insufficient data", __FUNCTION__);
		return -1; /* minimum attr size */
	}

	/*
	 *	We have an AES-128-CBC encrypted attribute
	 *
	 *	IV is from AT_IV, key is from k_encr.
	 *
	 *	unfortunately the ordering of these two attributes
	 *	aren't specified, so we may have to hunt for the IV.
	 */
	if (parent->flags.encrypt) {
		FR_PROTO_TRACE("found encrypted attribute '%s'", parent->name);

		decr_len = sim_value_decrypt(ctx, &decr, p + 2,
					     attr_len - 2, data_len - 2, decoder_ctx);	/* Skip reserved */
		if (decr_len < 0) return -1;

		p = decr;
		end = p + decr_len;
	} else {
		p += 2;	/* Skip the reserved bytes */
	}

	FR_PROTO_HEX_DUMP(p, end - p, "tlvs");

	/*
	 *  Record where we were in the list when packet_ctx function was called
	 */
	fr_cursor_init(&tlv_cursor, &head);
	while ((size_t)(end - p) >= sizeof(uint32_t)) {
		uint8_t	sim_at = p[0];
		size_t	sim_at_len = ((size_t)p[1]) << 2;

		if ((p + sim_at_len) > end) {
			fr_strerror_printf("%s: Malformed nested attribute %d: Length field (%zu bytes) value "
					   "longer than remaining data in parent (%zu bytes)",
					   __FUNCTION__, sim_at, sim_at_len, end - p);

		error:
			talloc_free(decr);
			fr_pair_list_free(&head);
			return -1;
		}

		if (sim_at_len == 0) {
			fr_strerror_printf("%s: Malformed nested attribute %d: Length field 0", __FUNCTION__, sim_at);
			goto error;
		}

		/*
		 *	Padding attributes are cleartext inside of
		 *	encrypted TLVs to pad out the value to the
		 *	correct length for the block cipher
		 *	(16 in the case of AES-128-CBC).
		 */
		if (sim_at == FR_SIM_PADDING) {
			uint8_t zero = 0;
			uint8_t i;

			if (!parent->flags.encrypt) {
				fr_strerror_printf("%s: Found padding attribute outside of an encrypted TLV",
						   __FUNCTION__);
				goto error;
			}

			if (!fr_cond_assert(data_len % 4)) goto error;

			if (sim_at_len > 12) {
				fr_strerror_printf("%s: Expected padding attribute length <= 12 bytes, got %zu bytes",
						   __FUNCTION__, sim_at_len);
				goto error;
			}

			/*
			 *	RFC says we MUST verify that FR_SIM_PADDING
			 *	data is zeroed out.
			 */
			for (i = 2; i < sim_at_len; i++) zero |= p[i];
			if (zero) {
				fr_strerror_printf("%s: Padding attribute value not zeroed 0x%pH", __FUNCTION__,
						   fr_box_octets(p + 2, sim_at_len - 2));
				goto error;
			}

			p += sim_at_len;
			continue;
		}

		child = fr_dict_attr_child_by_num(parent, p[0]);
		if (!child) {
			fr_dict_attr_t const *unknown_child;

			FR_PROTO_TRACE("Failed to find child %u of TLV %s", p[0], parent->name);

			/*
			 *	Encountered none skippable attribute
			 *
			 *	RFC says we need to die on these if we don't
			 *	understand them.  non-skippables are < 128.
			 */
			if (sim_at <= SIM_SKIPPABLE_MAX) {
				fr_strerror_printf("%s: Unknown (non-skippable) attribute %i",
						   __FUNCTION__, sim_at);
				goto error;
			}

			/*
			 *	Build an unknown attr
			 */
			unknown_child = fr_dict_unknown_afrom_fields(ctx, parent,
								     fr_dict_vendor_num_by_da(parent), p[0]);
			if (!unknown_child) goto error;
			child = unknown_child;
		}
		FR_PROTO_TRACE("decode context changed %s -> %s", parent->name, child->name);

		rcode = sim_decode_pair_value(ctx, &tlv_cursor, child, p + 2, sim_at_len - 2, (end - p) - 2,
					      decoder_ctx);
		if (rcode < 0) goto error;
		p += sim_at_len;
	}
	fr_cursor_head(&tlv_cursor);
	fr_cursor_tail(cursor);
	fr_cursor_merge(cursor, &tlv_cursor);	/* Wind to the end of the new pairs */
	talloc_free(decr);

	return attr_len;
}
Esempio n. 9
0
/** Decrypt an AES-128-CBC encrypted attribute
 *
 * @param[in] ctx		to allocate decr buffer in.
 * @param[out] out		where to write pointer to decr buffer.
 * @param[in] data		to decrypt.
 * @param[in] attr_len		length of encrypted data.
 * @param[in] data_len		length of data remaining in the packet.
 * @param[in] decoder_ctx	containing keys, and the IV (if we already found it).
 * @return
 *	- Number of decr bytes decrypted on success.
 *	- < 0 on failure.
 */
static ssize_t sim_value_decrypt(TALLOC_CTX *ctx, uint8_t **out,
				 uint8_t const *data, size_t const attr_len, size_t const data_len,
				 void *decoder_ctx)
{
	fr_sim_decode_ctx_t	*packet_ctx = decoder_ctx;
	EVP_CIPHER_CTX		*evp_ctx;
	EVP_CIPHER const	*evp_cipher = EVP_aes_128_cbc();
	size_t			block_size = EVP_CIPHER_block_size(evp_cipher);
	size_t			len = 0, decr_len = 0;
	uint8_t			*decr = NULL;

	if (!fr_cond_assert(attr_len <= data_len)) return -1;

	FR_PROTO_HEX_DUMP(data, attr_len, "ciphertext");

	/*
	 *	Encrypted values must be a multiple of 16.
	 *
	 *	There's a padding attribute to ensure they
	 *	always can be...
	 */
	if (attr_len % block_size) {
		fr_strerror_printf("%s: Encrypted attribute is not a multiple of cipher's block size (%zu)",
				   __FUNCTION__, block_size);
		return -1;
	}

	/*
	 *	Ugh, now we have to go hunting for it....
	 */
	if (!packet_ctx->have_iv) {
		uint8_t const	*p = data + attr_len;	/* Skip to the end of packet_ctx attribute */
		uint8_t const	*end = data + data_len;

		while ((size_t)(end - p) >= sizeof(uint32_t)) {
			uint8_t	 sim_at = p[0];
			size_t	 sim_at_len = p[1] * sizeof(uint32_t);

			if (sim_at_len == 0) {
				fr_strerror_printf("%s: Failed IV search.  AT Length field is zero", __FUNCTION__);
				return -1;
			}

			if ((p + sim_at_len) > end) {
				fr_strerror_printf("%s: Invalid IV length, longer than remaining data", __FUNCTION__);
				return -1;
			}

			if (sim_at == FR_SIM_IV) {
				if (sim_iv_extract(&(packet_ctx->iv[0]), p + 2, sim_at_len - 2) < 0) return -1;
				packet_ctx->have_iv = true;
				break;
			}
			p += sim_at_len;
		}

		if (!packet_ctx->have_iv) {
			fr_strerror_printf("%s: No IV present in packet, can't decrypt data", __FUNCTION__);
			return -1;
		}
	}

	evp_ctx = EVP_CIPHER_CTX_new();
	if (!evp_ctx) {
		tls_strerror_printf("%s: Failed initialising EVP ctx", __FUNCTION__);
		return -1;
	}

	if (!EVP_DecryptInit_ex(evp_ctx, evp_cipher, NULL, packet_ctx->keys->k_encr, packet_ctx->iv)) {
		tls_strerror_printf("%s: Failed setting decryption parameters", __FUNCTION__);
	error:
		talloc_free(decr);
		EVP_CIPHER_CTX_free(evp_ctx);
		return -1;
	}

	MEM(decr = talloc_zero_array(ctx, uint8_t, attr_len));

	/*
	 *	By default OpenSSL expects 16 bytes of cleartext
	 *	to produce 32 bytes of ciphertext, due to padding
	 *	being added if the decr is a multiple of 16.
	 *
	 *	There's no way for OpenSSL to determine if a
	 *	16 byte ciphertext was padded or not, so we need to
	 *	inform OpenSSL explicitly that there's no padding.
	 */
	EVP_CIPHER_CTX_set_padding(evp_ctx, 0);
	if (!EVP_DecryptUpdate(evp_ctx, decr, (int *)&len, data, attr_len)) {
		tls_strerror_printf("%s: Failed decrypting attribute", __FUNCTION__);
		goto error;
	}
	decr_len = len;

	if (!EVP_DecryptFinal_ex(evp_ctx, decr + decr_len, (int *)&len)) {
		tls_strerror_printf("%s: Failed decrypting attribute", __FUNCTION__);
		goto error;
	}
	decr_len += len;

	EVP_CIPHER_CTX_free(evp_ctx);

	/*
	 *	Note: packet_ctx implicitly validates the length of the padding
	 *	attribute (if present), so we don't have to do it later.
	 */
	if (decr_len % block_size) {
		fr_strerror_printf("%s: Expected decrypted value length to be multiple of %zu, got %zu",
				   __FUNCTION__, block_size, decr_len);
		goto error;
	}

	/*
	 *	Ciphertext should be same length as plaintext.
	 */
	if (unlikely(attr_len != decr_len)) {
		fr_strerror_printf("%s: Invalid plaintext length, expected %zu, got %zu",
				   __FUNCTION__, attr_len, decr_len);
		goto error;
	}

	FR_PROTO_TRACE("decryption successful, got %zu bytes of cleartext", decr_len);
	FR_PROTO_HEX_DUMP(decr, decr_len, "cleartext");

	*out = decr;

	return decr_len;
}