コード例 #1
0
/** Builds attribute representing OID string and adds 'index' attributes where required
 *
 * Will convert an OID string in the format @verbatim .1.2.3.4.5.0 @endverbatim
 * into a pair with a #fr_dict_attr_t of the dictionary attribute matching the OID
 * string, as evaluated from the specified parent.
 *
 * If an OID component does not match a child of a previous OID component, but a child
 * with attribute number 0 exists, and a child with attribute number 1 also exists,
 * the child with attribute number 0 will be used as an 'index' pair, and will be
 * created with the value of the non matching OID component.
 *
 * Parsing will then resume using the child with attribute number 1.
 *
 * This allows traversals of SNMP tables to be represented by the sequence of pairs
 * and allows the full range of entry indexes which would not be possible if we represented
 * table index numbers as TLV attributes.
 *
 * @param[in] ctx	to allocate new pairs in.
 * @param[in] conf	radsnmp config.
 * @param[in] cursor	to add pairs to.
 * @param[in] oid	string to evaluate.
 * @param[in] type	SNMP value type.
 * @param[in] value	to assign to OID attribute (SET operations only).
 * @return
 *	- >0 on success (how much of the OID string we parsed).
 *	- <=0 on failure (where format error occurred).
 */
static ssize_t radsnmp_pair_from_oid(TALLOC_CTX *ctx, radsnmp_conf_t *conf, fr_cursor_t *cursor,
				     char const *oid, int type, char const *value)
{
	ssize_t			slen;
	char const		*p = oid;
	unsigned int		attr;
	fr_dict_attr_t const	*index_attr, *da;
	fr_dict_attr_t const	*parent = conf->snmp_root;
	VALUE_PAIR		*vp;
	int			ret;

	if (!oid) return 0;

	fr_cursor_tail(cursor);

	/*
	 *	Trim first.
	 */
	if (p[0] == '.') p++;

	/*
	 *	Support for indexes.  If we can't find an attribute
	 *	matching a child at a given level in the OID tree,
	 *	look for attribute 0 (type integer) at that level.
	 *	We use this to represent the index instead.
	 */
	for (;;) {
		unsigned int num = 0;

		slen = fr_dict_attr_by_oid(conf->dict, &parent, &attr, p);
		if (slen > 0) break;
		p += -(slen);

		if (fr_dict_oid_component(&num, &p) < 0) break;	/* Just advances the pointer */
		assert(attr == num);
		p++;

		/*
		 *	Check for an index attribute
		 */
		index_attr = fr_dict_attr_child_by_num(parent, 0);
		if (!index_attr) {
			fr_strerror_printf("Unknown OID component: No index attribute at this level");
			break;
		}

		if (index_attr->type != FR_TYPE_UINT32) {
			fr_strerror_printf("Index is not a \"integer\"");
			break;
		}

		/*
		 *	By convention SNMP entries are at .1
		 */
		parent = fr_dict_attr_child_by_num(parent, 1);
		if (!parent) {
			fr_strerror_printf("Unknown OID component: No entry attribute at this level");
			break;
		}

		/*
		 *	Entry must be a TLV type
		 */
		if (parent->type != FR_TYPE_TLV) {
			fr_strerror_printf("Entry is not \"tlv\"");
			break;
		}

		/*
		 *	We've skipped over the index attribute, and
		 *	the index number should be available in attr.
		 */
		MEM(vp = fr_pair_afrom_da(ctx, index_attr));
		vp->vp_uint32 = attr;

		fr_cursor_append(cursor, vp);
	}

	/*
	 *	We errored out processing the OID.
	 */
	if (slen <= 0) {
	error:
		fr_cursor_free_list(cursor);
		return slen;
	}

	fr_strerror();	/* Clear pending errors */

	/*
	 *	SNMP requests the leaf under the OID with .0.
	 */
	if (attr != 0) {
		da = fr_dict_attr_child_by_num(parent, attr);
		if (!da) {
			fr_strerror_printf("Unknown leaf attribute %i", attr);
			return -(slen);
		}
	} else {
		da = parent;
	}

	vp = fr_pair_afrom_da(ctx, da);
	if (!vp) {
		fr_strerror_printf("Failed allocating OID attribute");
		return -(slen);
	}

	/*
	 *	VALUE_PAIRs with no value need a 1 byte value buffer.
	 */
	if (!value) {
		switch (da->type) {
		/*
		 *	We can blame the authors of RFC 6929 for
		 *	this hack.  Apparently the presence or absence
		 *	of an attribute isn't considered a useful means
		 *	of conveying information, so empty TLVs are
		 *	disallowed.
		 */
		case FR_TYPE_TLV:
			fr_pair_to_unknown(vp);
			/* FALL-THROUGH */

		case FR_TYPE_OCTETS:
			fr_pair_value_memcpy(vp, (uint8_t const *)"\0", 1, true);
			break;

		case FR_TYPE_STRING:
			fr_pair_value_bstrncpy(vp, "\0", 1);
			break;

		/*
		 *	Fine to leave other values zeroed out.
		 */
		default:
			break;
		}

		fr_cursor_append(cursor, vp);
		return slen;
	}

	if (da->type == FR_TYPE_TLV) {
		fr_strerror_printf("TLVs cannot hold values");
		return -(slen);
	}

	ret = fr_pair_value_from_str(vp, value, strlen(value), '\0', true);
	if (ret < 0) {
		slen = -(slen);
		goto error;
	}

	vp = fr_pair_afrom_da(ctx, attr_freeradius_snmp_type);
	if (!vp) {
		slen = -(slen);
		goto error;
	}
	vp->vp_uint32 = type;

	fr_cursor_append(cursor, vp);

	return slen;
}
コード例 #2
0
/*
 *	build a reply to be sent.
 */
static int eap_sim_compose(eap_session_t *eap_session, uint8_t const *hmac_extra, size_t hmac_extra_len)
{
	eap_sim_session_t	*eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
	fr_cursor_t		cursor;
	fr_cursor_t		to_encode;
	VALUE_PAIR		*head = NULL, *vp;
	REQUEST			*request = eap_session->request;
	fr_sim_encode_ctx_t	encoder_ctx = {
					.root = fr_dict_root(dict_eap_sim),
					.keys = &eap_sim_session->keys,

					.iv = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
						0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
					.iv_included = false,

					.hmac_md = EVP_sha1(),
					.eap_packet = eap_session->this_round->request,
					.hmac_extra = hmac_extra,
					.hmac_extra_len = hmac_extra_len
				};

	ssize_t			ret;

	/* we will set the ID on requests, since we have to HMAC it */
	eap_session->this_round->set_request_id = true;

	fr_cursor_init(&cursor, &eap_session->request->reply->vps);
	fr_cursor_init(&to_encode, &head);

	while ((vp = fr_cursor_current(&cursor))) {
		if (!fr_dict_parent_common(fr_dict_root(dict_eap_sim), vp->da, true)) {
			fr_cursor_next(&cursor);
			continue;
		}
		vp = fr_cursor_remove(&cursor);

		/*
		 *	Silently discard encrypted attributes until
		 *	the peer should have k_encr.  These can be
		 *	added by policy, and seem to cause
		 *	wpa_supplicant to fail if sent before the challenge.
		 */
		if (!eap_sim_session->allow_encrypted && fr_dict_parent_common(attr_eap_sim_encr_data, vp->da, true)) {
			RWDEBUG("Silently discarding &reply:%s: Encrypted attributes not allowed in this round",
				vp->da->name);
			talloc_free(vp);
			continue;
		}

		fr_cursor_append(&to_encode, vp);
	}

	RDEBUG2("Encoding EAP-SIM attributes");
	log_request_pair_list(L_DBG_LVL_2, request, head, NULL);

	eap_session->this_round->request->type.num = FR_EAP_SIM;
	eap_session->this_round->request->id = eap_sim_session->sim_id++ & 0xff;
	eap_session->this_round->set_request_id = true;

	ret = fr_sim_encode(eap_session->request, head, &encoder_ctx);
	fr_cursor_head(&to_encode);
	fr_cursor_free_list(&to_encode);

	if (ret < 0) {
		RPEDEBUG("Failed encoding EAP-SIM data");
		return -1;
	}
	return 0;
}

static int eap_sim_send_start(eap_session_t *eap_session)
{
	REQUEST			*request = eap_session->request;
	VALUE_PAIR		**vps, *vp;
	uint16_t		version;
	eap_sim_session_t	*eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
	RADIUS_PACKET		*packet;

	rad_assert(eap_session->request != NULL);
	rad_assert(eap_session->request->reply);

	RDEBUG2("Sending SIM-State");
	eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
	eap_sim_session->allow_encrypted = false;	/* In case this is after failed fast-resumption */

	/* these are the outgoing attributes */
	packet = eap_session->request->reply;
	vps = &packet->vps;
	rad_assert(vps != NULL);

	/*
	 *	Add appropriate TLVs for the EAP things we wish to send.
	 */
	vp = fr_pair_afrom_da(packet, attr_eap_sim_version_list);
	vp->vp_uint16 = EAP_SIM_VERSION;
	fr_pair_add(vps, vp);

	/* record it in the ess */
	version = htons(EAP_SIM_VERSION);
	memcpy(eap_sim_session->keys.gsm.version_list, &version, sizeof(version));
	eap_sim_session->keys.gsm.version_list_len = 2;

	/*
	 *	Select the right type of identity request attribute
	 */
	switch (eap_sim_session->id_req) {
	case SIM_ANY_ID_REQ:
		vp = fr_pair_afrom_da(packet, attr_eap_sim_any_id_req);
		break;

	case SIM_PERMANENT_ID_REQ:
		vp = fr_pair_afrom_da(packet, attr_eap_sim_permanent_id_req);
		break;

	case SIM_FULLAUTH_ID_REQ:
		vp = fr_pair_afrom_da(packet, attr_eap_sim_fullauth_id_req);
		break;

	default:
		rad_assert(0);
	}
	vp->vp_bool = true;
	fr_pair_replace(vps, vp);

	/* the SUBTYPE, set to start. */
	vp = fr_pair_afrom_da(packet, attr_eap_sim_subtype);
	vp->vp_uint16 = EAP_SIM_START;
	fr_pair_replace(vps, vp);

	/*
	 *	Encode the packet
	 */
	if (eap_sim_compose(eap_session, NULL, 0) < 0) {
		fr_pair_list_free(&packet->vps);
		return -1;
	}

	return 0;
}
コード例 #3
0
ファイル: decode.c プロジェクト: FreeRADIUS/freeradius-server
/** Decode SIM/AKA/AKA' specific packet data
 *
 * @note data should point to the subtype field in the EAP packet.
 *
 * Extracts the SUBTYPE and adds it an attribute, then decodes any TLVs in the
 * SIM/AKA/AKA' packet.
 *
 *   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
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *  |     Code      |  Identifier   |            Length             |
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *  |     Type      |    Subtype    |           Reserved            |
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *
 * The first byte of the data pointer should be the subtype.
 *
 * @param[in] request		the current request.
 * @param[in] decoded		where to write decoded attributes.
 * @param[in] dict		for looking up attributes.
 * @param[in] data		to convert to pairs.
 * @param[in] data_len		length of data to convert.
 * @param[in] decoder_ctx	holds the state of the decoder.
 * @return
 *	- 0 on success.
 *	- -1 on failure.
 */
int fr_sim_decode(REQUEST *request, fr_cursor_t *decoded, fr_dict_t const *dict,
		  uint8_t const *data, size_t data_len, fr_sim_decode_ctx_t *decoder_ctx)
{
	ssize_t			rcode;
	uint8_t	const		*p = data;
	uint8_t const		*end = p + data_len;

	/*
	 *	Move the cursor to the end, so we know if
	 *	any additional attributes were added.
	 */
	fr_cursor_tail(decoded);

	/*
	 *	We need at least enough data for the subtype
	 *	and reserved bytes.
	 *
	 *	Note: Not all packets should contain attrs.
	 *	When the client acknowledges an
	 *	AKA-Notification from the server, the
	 *	AKA-Notification is returns contains no
	 *	attributes.
	 */
	if (data_len < 3) {
		fr_strerror_printf("Packet data too small, expected at least 3 bytes got %zu bytes", data_len);
		return -1;
	}
	p += 3;

	/*
	 *	Loop over all the attributes decoding
	 *	them into the appropriate attributes
	 *	in the SIM/AKA/AKA' dict.
	 */
	while (p < end) {
		rcode = fr_sim_decode_pair(request->packet, decoded, dict, p, end - p, decoder_ctx);
		if (rcode <= 0) {
			RPEDEBUG("Failed decoding AT");
		error:
			fr_cursor_free_list(decoded);	/* Free any attributes we added */
			return -1;
		}

		p += rcode;
		rad_assert(p <= end);
	}

	/*
	 *	No point in doing packet_ctx until we known the rest
	 *	of the data is OK!
	 */
	{
		VALUE_PAIR *vp;

		vp = fr_pair_afrom_child_num(request->packet, fr_dict_root(dict), FR_SIM_SUBTYPE);
		if (!vp) {
			fr_strerror_printf("Failed allocating subtype attribute");
			goto error;
		}
		vp->vp_uint32 = data[0];
		fr_cursor_append(decoded, vp);
	}

	return 0;
}
コード例 #4
0
static int eap_aka_compose(eap_session_t *eap_session)
{
	eap_aka_session_t	*eap_aka_session = talloc_get_type_abort(eap_session->opaque, eap_aka_session_t);
	fr_cursor_t		cursor;
	fr_cursor_t		to_encode;
	VALUE_PAIR		*head = NULL, *vp;
	REQUEST			*request = eap_session->request;
	ssize_t			ret;
	fr_sim_encode_ctx_t	encoder_ctx = {
					.root = fr_dict_root(dict_eap_aka),
					.keys = &eap_aka_session->keys,

					.iv = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
						0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
					.iv_included = false,

					.hmac_md = eap_aka_session->mac_md,
					.eap_packet = eap_session->this_round->request,
					.hmac_extra = NULL,
					.hmac_extra_len = 0
				};

	fr_cursor_init(&cursor, &eap_session->request->reply->vps);
	fr_cursor_init(&to_encode, &head);

	while ((vp = fr_cursor_current(&cursor))) {
		if (!fr_dict_parent_common(encoder_ctx.root, vp->da, true)) {
			fr_cursor_next(&cursor);
			continue;
		}
		vp = fr_cursor_remove(&cursor);

		/*
		 *	Silently discard encrypted attributes until
		 *	the peer should have k_encr.  These can be
		 *	added by policy, and seem to cause
		 *	wpa_supplicant to fail if sent before the challenge.
		 */
		if (!eap_aka_session->allow_encrypted && fr_dict_parent_common(attr_eap_aka_encr_data, vp->da, true)) {
			RWDEBUG("Silently discarding &reply:%s: Encrypted attributes not allowed in this round",
				vp->da->name);
			talloc_free(vp);
			continue;
		}

		fr_cursor_append(&to_encode, vp);
	}

	RDEBUG2("Encoding EAP-AKA attributes");
	log_request_pair_list(L_DBG_LVL_2, request, head, NULL);

	eap_session->this_round->request->type.num = eap_aka_session->type;
	eap_session->this_round->request->id = eap_aka_session->aka_id++ & 0xff;
	eap_session->this_round->set_request_id = true;

	ret = fr_sim_encode(eap_session->request, head, &encoder_ctx);
	fr_cursor_head(&to_encode);
	fr_cursor_free_list(&to_encode);

	if (ret < 0) {
		RPEDEBUG("Failed encoding EAP-AKA data");
		return -1;
	}
	return 0;
}

/** Send an EAP-AKA identity request to the supplicant
 *
 * There are three types of user identities that can be implemented
 * - Permanent identities such as [email protected]
 *   Permanent identities can be identified by the leading zero followed by
 *   by 15 digits (the IMSI number).
 * - Ephemeral identities (pseudonyms).  These are identities assigned for
 *   identity privacy so the user can't be tracked.  These can identities
 *   can either be generated as per the 3GPP 'Security aspects of non-3GPP accesses'
 *   document section 14, where a set of up to 16 encryption keys are used
 *   to reversibly encrypt the IMSI. Alternatively the pseudonym can be completely
 *   randomised and stored in a datastore.
 * - A fast resumption ID which resolves to data used for fast resumption.
 *
 * In order to perform full authentication the original IMSI is required for
 * forwarding to the HLR. In the case where we can't match/decrypt the pseudonym,
 * or can't perform fast resumption, we need to request the full identity from
 * the supplicant.
 *
 * @param[in] eap_session	to continue.
 * @return
 *	- 0 on success.
 *	- <0 on failure.
 */
static int eap_aka_send_identity_request(eap_session_t *eap_session)
{
	REQUEST			*request = eap_session->request;
	eap_aka_session_t	*eap_aka_session = talloc_get_type_abort(eap_session->opaque, eap_aka_session_t);
	VALUE_PAIR		*vp;
	RADIUS_PACKET		*packet;
	fr_cursor_t		cursor;

	RDEBUG2("Sending AKA-Identity (%s)", fr_int2str(sim_id_request_table, eap_aka_session->id_req, "<INVALID>"));
	eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
	eap_aka_session->allow_encrypted = false;	/* In case this is after failed fast-resumption */

	packet = request->reply;
	fr_cursor_init(&cursor, &packet->vps);

	/*
	 *	Set the subtype to identity request
	 */
	vp = fr_pair_afrom_da(packet, attr_eap_aka_subtype);
	vp->vp_uint16 = FR_EAP_AKA_SUBTYPE_VALUE_AKA_IDENTITY;
	fr_cursor_append(&cursor, vp);

	/*
	 *	Select the right type of identity request attribute
	 */
	switch (eap_aka_session->id_req) {
	case SIM_ANY_ID_REQ:
		vp = fr_pair_afrom_da(packet, attr_eap_aka_any_id_req);
		break;

	case SIM_PERMANENT_ID_REQ:
		vp = fr_pair_afrom_da(packet, attr_eap_aka_permanent_id_req);
		break;

	case SIM_FULLAUTH_ID_REQ:
		vp = fr_pair_afrom_da(packet, attr_eap_aka_fullauth_id_req);
		break;

	default:
		rad_assert(0);
	}
	vp->vp_bool = true;
	fr_cursor_append(&cursor, vp);

	/*
	 *	Encode the packet
	 */
	if (eap_aka_compose(eap_session) < 0) {
	failure:
		fr_pair_list_free(&packet->vps);
		return -1;
	}

	/*
	 *	Digest the packet contents, updating our checkcode.
	 */
	if (!eap_aka_session->checkcode_state &&
	    fr_sim_crypto_init_checkcode(eap_aka_session, &eap_aka_session->checkcode_state,
	    				 eap_aka_session->checkcode_md) < 0) {
		RPEDEBUG("Failed initialising checkcode");
		goto failure;
	}
	if (fr_sim_crypto_update_checkcode(eap_aka_session->checkcode_state, eap_session->this_round->request) < 0) {
		RPEDEBUG("Failed updating checkcode");
		goto failure;
	}

	return 0;
}