예제 #1
0
int8_t fr_dhcpv4_attr_cmp(void const *a, void const *b)
{
	VALUE_PAIR const *my_a = a, *my_b = b;
	fr_dict_attr_t const *a_82, *b_82;

	VP_VERIFY(my_a);
	VP_VERIFY(my_b);

	/*
	 *	We can only use attribute numbers if we know they're
	 *	not nested attributes.
	 *
	 *	@fixme We should be able to use my_a->da->parent->flags.is_root,
	 *	but the DHCP attributes are hacked into the server under a vendor
	 *	dictionary, so we can't.
	 */

	/*
	 *	DHCP-Message-Type is first, for simplicity.
	 */
	if (((my_a->da->parent->type != FR_TYPE_TLV) && (my_a->da == attr_dhcp_message_type)) &&
	    ((my_b->da->parent->type == FR_TYPE_TLV) || (my_b->da != attr_dhcp_message_type))) return -1;
	if (((my_a->da->parent->type == FR_TYPE_TLV) || (my_a->da != attr_dhcp_message_type)) &&
	    ((my_b->da->parent->type != FR_TYPE_TLV) && (my_b->da == attr_dhcp_message_type))) return +1;

	/*
	 *	Relay-Agent is last.
	 *
	 *	Check if either of the options are descended from option 82.
	 */
	a_82 = fr_dict_parent_common(dhcp_option_82, my_a->da, true);
	b_82 = fr_dict_parent_common(dhcp_option_82, my_b->da, true);
	if (a_82 && !b_82) return +1;
	if (!a_82 && !b_82) return -1;

	return fr_pair_cmp_by_parent_num_tag(my_a, my_b);
}
예제 #2
0
/** Iterate over attributes with a given ancestor
 *
 * Find the next attribute of a given type. If no fr_pair_cursor_next_by_* function
 * has been called on a cursor before, or the previous call returned
 * NULL, the search will start with the current attribute. Subsequent calls to
 * fr_pair_cursor_next_by_* functions will start the search from the previously
 * matched attribute.
 *
 * @param cursor to operate on.
 * @param ancestor attribute to match on.
 * @param tag to match. Either a tag number or TAG_ANY to match any tagged or
 *	  untagged attribute, TAG_NONE to match attributes without tags.
 * @return
 *	- Next matching #VALUE_PAIR.
 *	- NULL if no #VALUE_PAIR (s) match.
 */
VALUE_PAIR *fr_pair_cursor_next_by_ancestor(vp_cursor_t *cursor, fr_dict_attr_t const *ancestor, int8_t tag)
{
	VALUE_PAIR *i;

	if (!cursor->first) return NULL;

	for (i = cursor->found ? cursor->found->next : cursor->current;
	     i != NULL;
	     i = i->next) {
		VP_VERIFY(i);
		if (fr_dict_parent_common(ancestor, i->da, true) &&
		    ATTR_TAG_MATCH(i, tag)) {
			break;
		}
	}

	return fr_pair_cursor_update(cursor, i);
}
예제 #3
0
/** Write the result of a get or getnext operation back to net-snmp
 *
 * Returns three lines of output per attribute:
 * - OID string
 * - type
 * - value
 *
 * Index attributes (num 0) must be in order of depth (shallowest first).
 *
 * If no attributes were written, will write "NONE\n" to inform net-snmp
 * that no value was available at the specified OID.
 *
 * @param fd to write to.
 * @param root of the SNMP portion of the main dictionary.
 * @param type attribute.
 * @param head of list of attributes to convert and write.
 * @return
 *	- >=0 on success (the number of varbind responses written).
 *	- -1 on failure.
 */
static int radsnmp_get_response(int fd,
				fr_dict_attr_t const *root, fr_dict_attr_t const *type,
				VALUE_PAIR *head)
{
	fr_cursor_t		cursor;
	VALUE_PAIR		*vp, *type_vp;
	fr_dict_attr_t const	*parent = root;
	unsigned int		written = 0;

	ssize_t			slen;
	size_t			len;

	char			type_buff[32];	/* type */
	size_t			type_len = 0;
	char			oid_buff[256];
	char			value_buff[128];
	char			*p = oid_buff, *end = p + sizeof(oid_buff);

	struct iovec		io_vector[6];

	char			newline[] = "\n";

	type_buff[0] = '\0';

	/*
	 *	Print first part of OID string.
	 */
	slen = snprintf(oid_buff, sizeof(oid_buff), "%u.", parent->attr);
	if (is_truncated((size_t)slen, sizeof(oid_buff))) {
	oob:
		fr_strerror_printf("OID Buffer too small");
		return -1;
	}
	p += slen;

	/*
	 *	@fixme, this is very dependent on ordering
	 *
	 *	This code should be reworked when we have proper
	 *	attribute grouping to coalesce all related index
	 *	attributes under a single request OID.
	 */
	 for (vp = fr_cursor_init(&cursor, &head);
	      vp;
	      vp = fr_cursor_next(&cursor)) {
	      	fr_dict_attr_t const *common;
	      	/*
	      	 *	We only care about TLV attributes beneath our root
	      	 */
		if (!fr_dict_parent_common(root, vp->da, true)) continue;

		/*
		 *	Sanity checks to ensure we're processing attributes
		 *	in the right order.
		 */
		common = fr_dict_parent_common(parent, vp->da, true);
		if (!common) {
			fr_strerror_printf("Out of order index attributes.  \"%s\" is not a child of \"%s\"",
					   vp->da->name, parent->name);
			return -1;
		}

		/*
		 *	Index attribute
		 */
		if (vp->da->attr == 0) {
			/*
			 *	Print OID from last index/root up to the parent of
			 *	the index attribute.
			 */
			slen = fr_dict_print_attr_oid(p, end - p, parent, vp->da->parent);
			if (slen < 0) return -1;

			if (vp->vp_type != FR_TYPE_UINT32) {
				fr_strerror_printf("Index attribute \"%s\" is not of type \"integer\"", vp->da->name);
				return -1;
			}

			if (slen >= (end - p)) goto oob;
			p += slen;

			/*
			 *	Add the value of the index attribute as the next
			 *	OID component.
			 */
			len = snprintf(p, end - p, ".%i.", vp->vp_uint32);
			if (is_truncated(len, end - p)) goto oob;

			p += len;

			/*
			 *	Set the parent to be the attribute representing
			 *	the entry.
			 */
			parent = fr_dict_attr_child_by_num(vp->da->parent, 1);
			continue;
		}

		/*
		 *	Actual TLV attribute
		 */
		slen = fr_dict_print_attr_oid(p, end - p, parent, vp->da);
		if (slen < 0) return -1;

		/*
		 *	Next attribute should be the type
		 */
		type_vp = fr_cursor_next(&cursor);
		if (!type_vp || (type_vp->da != type)) {
			fr_strerror_printf("No %s found in response, or occurred out of order", type->name);
			return -1;
		}
		type_len = fr_pair_value_snprint(type_buff, sizeof(type_buff), type_vp, '\0');

		/*
		 *	Build up the vector
		 *
		 *	This represents output for a single varbind attribute
		 */
		io_vector[0].iov_base = oid_buff;
		io_vector[0].iov_len = strlen(oid_buff);
		io_vector[1].iov_base = newline;
		io_vector[1].iov_len = 1;
		io_vector[2].iov_base = type_buff;
		io_vector[2].iov_len = type_len;
		io_vector[3].iov_base = newline;
		io_vector[3].iov_len = 1;

		switch (vp->vp_type) {
		case FR_TYPE_OCTETS:
			memcpy(&io_vector[4].iov_base, &vp->vp_strvalue, sizeof(io_vector[4].iov_base));
			io_vector[4].iov_len = vp->vp_length;
			break;

		case FR_TYPE_STRING:
			memcpy(&io_vector[4].iov_base, &vp->vp_strvalue, sizeof(io_vector[4].iov_base));
			io_vector[4].iov_len = vp->vp_length;
			break;

		default:
			/*
			 *	We call fr_value_box_snprint with a NULL da pointer
			 *	because we always need return integer values not
			 *	value aliases.
			 */
			len = fr_value_box_snprint(value_buff, sizeof(value_buff), &vp->data, '\0');
			if (is_truncated(len, sizeof(value_buff))) {
				fr_strerror_printf("Insufficient fixed value buffer");
				return -1;
			}
			io_vector[4].iov_base = value_buff;
			io_vector[4].iov_len = len;
			break;
		}
		io_vector[5].iov_base = newline;
		io_vector[5].iov_len = 1;

		DEBUG2("said: %s", (char *)io_vector[0].iov_base);
		DEBUG2("said: %s", (char *)io_vector[2].iov_base);
		DEBUG2("said: %s", (char *)io_vector[4].iov_base);

		if (writev(fd, io_vector, sizeof(io_vector) / sizeof(*io_vector)) < 0) {
			fr_strerror_printf("Failed writing varbind result: %s", fr_syserror(errno));
			return -1;
		}

		/*
		 *	Reset in case we're encoding multiple values
		 */
		parent = root;
		p = oid_buff;
		type_buff[0] = '\0';
		written++;
	}

	if (!written && (write(fd, "NONE\n", 5)) < 0) {
		fr_strerror_printf("Failed writing get response: %s", fr_syserror(errno));
		return -1;
	}

	return written;
}
예제 #4
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;
}
예제 #5
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;
}