Beispiel #1
0
/** Send a failure message
 *
 */
static int eap_sim_send_eap_failure_notification(eap_session_t *eap_session)
{
	REQUEST			*request = eap_session->request;
	RADIUS_PACKET		*packet = eap_session->request->reply;
	fr_cursor_t		cursor;
	VALUE_PAIR		*vp;
	eap_sim_session_t	*eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);

	fr_cursor_init(&cursor, &packet->vps);

	vp = fr_pair_find_by_da(packet->vps, attr_eap_sim_notification, TAG_ANY);
	if (!vp) {
		vp = fr_pair_afrom_da(packet, attr_eap_sim_notification);
		vp->vp_uint16 = FR_EAP_SIM_NOTIFICATION_VALUE_GENERAL_FAILURE;
		fr_cursor_append(&cursor, vp);
	}

	/*
	 *	Change the failure notification depending where
	 *	we are in the state machine.
	 */
	if (eap_sim_session->challenge_success) {
		vp->vp_uint16 &= ~0x4000;	/* Unset phase bit */
	} else {
		vp->vp_uint16 |= 0x4000;	/* Set phase bit */
	}
	vp->vp_uint16 &= ~0x8000;               /* In both cases success bit should be low */

	RDEBUG2("Sending SIM-Notification (%pV)", &vp->data);
	eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;

	/*
	 *	Set the subtype to notification
	 */
	vp = fr_pair_afrom_da(packet, attr_eap_sim_subtype);
	vp->vp_uint16 = FR_EAP_SIM_SUBTYPE_VALUE_SIM_NOTIFICATION;
	fr_cursor_append(&cursor, vp);

	/*
	 *	If we're after the challenge phase
	 *	then we need to include a MAC to
	 *	protect notifications.
	 */
	if (eap_sim_session->challenge_success) {
		vp = fr_pair_afrom_da(packet, attr_eap_sim_mac);
		fr_pair_replace(&packet->vps, vp);
	}

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

	return 0;
}
Beispiel #2
0
/** Add a child
 *
 * @param[in] parent	to add child to.
 * @param[in] child	to add.
 */
void _cf_item_add(CONF_ITEM *parent, CONF_ITEM *child)
{
	fr_cursor_t	to_merge;
	CONF_ITEM	*ci;

	rad_assert(parent != child);

	if (!parent || !child) return;

	/*
	 *	New child, add child trees.
	 */
	if (!parent->ident1) parent->ident1 = rbtree_create(parent, _cf_ident1_cmp, NULL, RBTREE_FLAG_NONE);
	if (!parent->ident2) parent->ident2 = rbtree_create(parent, _cf_ident2_cmp, NULL, RBTREE_FLAG_NONE);

	fr_cursor_init(&to_merge, &child);

	for (ci = fr_cursor_head(&to_merge);
	     ci;
	     ci = fr_cursor_next(&to_merge)) {
		rbtree_insert(parent->ident1, ci);
		rbtree_insert(parent->ident2, ci);	/* NULL ident2 is still a value */
	 	fr_cursor_append(&parent->cursor, ci);	/* Append to the list of children */
	}
}
Beispiel #3
0
static int _map_proc_client_get_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request,
				   vp_map_t const *map, void *uctx)
{
	client_get_vp_ctx_t	*client = uctx;
	VALUE_PAIR		*head = NULL, *vp;
	fr_cursor_t		cursor;
	fr_dict_attr_t const	*da;
	CONF_PAIR const		*cp;

	rad_assert(ctx != NULL);

	fr_cursor_init(&cursor, &head);

	/*
	 *	FIXME: allow multiple entries.
	 */
	if (map->lhs->type == TMPL_TYPE_ATTR) {
		da = map->lhs->tmpl_da;
	} else {
		char *attr;

		if (tmpl_aexpand(ctx, &attr, request, map->lhs, NULL, NULL) <= 0) {
			RWDEBUG("Failed expanding string");
			return -1;
		}

		da = fr_dict_attr_by_name(request->dict, attr);
		if (!da) {
			RWDEBUG("No such attribute '%s'", attr);
			return -1;
		}

		talloc_free(attr);
	}

	for (cp = client->cp;
	     cp;
	     cp = cf_pair_find_next(client->cs, cp, client->field)) {
		char const *value = cf_pair_value(cp);

		MEM(vp = fr_pair_afrom_da(ctx, da));
		if (fr_pair_value_from_str(vp, value, talloc_array_length(value) - 1, '\0', false) < 0) {
			RWDEBUG("Failed parsing value \"%pV\" for attribute %s: %s", fr_box_strvalue(value),
				map->lhs->tmpl_da->name, fr_strerror());
			fr_pair_list_free(&head);
			talloc_free(vp);
			return -1;
		}

		vp->op = map->op;
		fr_cursor_append(&cursor, vp);

		if (map->op != T_OP_ADD) break;	/* Create multiple attribute for multiple CONF_PAIRs */
	}

	*out = head;

	return 0;
}
Beispiel #4
0
/** Send a success notification
 *
 */
static int eap_sim_send_eap_success_notification(eap_session_t *eap_session)
{
	REQUEST			*request = eap_session->request;
	RADIUS_PACKET		*packet = eap_session->request->reply;
	eap_sim_session_t	*eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
	fr_cursor_t		cursor;
	VALUE_PAIR		*vp;

	RDEBUG2("Sending SIM-Notification (Success)");
	eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;

	if (!fr_cond_assert(eap_sim_session->challenge_success)) return -1;

	fr_cursor_init(&cursor, &packet->vps);

	/*
	 *	Set the subtype to notification
	 */
	vp = fr_pair_afrom_da(packet, attr_eap_sim_subtype);
	vp->vp_uint16 = FR_EAP_SIM_SUBTYPE_VALUE_SIM_NOTIFICATION;
	fr_cursor_append(&cursor, vp);

	vp = fr_pair_afrom_da(packet, attr_eap_sim_notification);
	vp->vp_uint16 = FR_EAP_SIM_NOTIFICATION_VALUE_SUCCESS;
	fr_cursor_append(&cursor, vp);

	/*
	 *	Need to include an AT_MAC attribute so that it will get
	 *	calculated.
	 */
	vp = fr_pair_afrom_da(packet, attr_eap_sim_mac);
	fr_pair_replace(&packet->vps, vp);

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

	return 0;
}
Beispiel #5
0
/*
 *	Convert field X to a VP.
 */
static int csv_map_getvalue(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map, void *uctx)
{
	char const		*str = uctx;
	VALUE_PAIR		*head = NULL, *vp;
	fr_cursor_t		cursor;
	fr_dict_attr_t		const *da;

	rad_assert(ctx != NULL);
	fr_cursor_init(&cursor, &head);

	/*
	 *	FIXME: allow multiple entries.
	 */
	if (map->lhs->type == TMPL_TYPE_ATTR) {
		da = map->lhs->tmpl_da;

	} else {
		char *attr;

		if (tmpl_aexpand(ctx, &attr, request, map->lhs, NULL, NULL) <= 0) {
			RWDEBUG("Failed expanding string");
			return -1;
		}


		da = fr_dict_attr_by_name(request->dict, attr);
		if (!da) {
			RWDEBUG("No such attribute '%s'", attr);
			return -1;
		}

		talloc_free(attr);
	}

	vp = fr_pair_afrom_da(ctx, da);
	rad_assert(vp);

	if (fr_pair_value_from_str(vp, str, talloc_array_length(str) - 1, '\0', true) < 0) {
		RWDEBUG("Failed parsing value \"%pV\" for attribute %s: %s", fr_box_strvalue_buffer(str),
			map->lhs->tmpl_da->name, fr_strerror());
		talloc_free(vp);

		return -1;
	}

	vp->op = map->op;
	fr_cursor_append(&cursor, vp);

	*out = head;
	return 0;
}
Beispiel #6
0
static xlat_action_t redis_remap_xlat(TALLOC_CTX *ctx, fr_cursor_t *out,
				      REQUEST *request, void const *xlat_inst,
				      UNUSED void *xlat_thread_inst,
				      fr_value_box_t **in)
{
	rlm_redis_t const		*inst = talloc_get_type_abort_const(*((void const * const *)xlat_inst),
									    rlm_redis_t);

	fr_socket_addr_t		node_addr;
	fr_pool_t			*pool;
	fr_redis_conn_t			*conn;
	fr_redis_cluster_rcode_t	rcode;
	fr_value_box_t			*vb;

	if (!in) {
		REDEBUG("Missing key");
		return XLAT_ACTION_FAIL;
	}

	if (fr_value_box_list_concat(ctx, *in, in, FR_TYPE_STRING, true) < 0) {
		RPEDEBUG("Failed concatenating input");
		return XLAT_ACTION_FAIL;
	}

	if (fr_inet_pton_port(&node_addr.ipaddr, &node_addr.port, (*in)->vb_strvalue, (*in)->vb_length,
			      AF_UNSPEC, true, true) < 0) {
		RPEDEBUG("Failed parsing node address");
		return XLAT_ACTION_FAIL;
	}

	if (fr_redis_cluster_pool_by_node_addr(&pool, inst->cluster, &node_addr, true) < 0) {
		RPEDEBUG("Failed locating cluster node");
		return XLAT_ACTION_FAIL;
	}

	conn = fr_pool_connection_get(pool, request);
	if (!conn) {
		REDEBUG("No connections available for cluster node");
		return XLAT_ACTION_FAIL;
	}

	rcode = fr_redis_cluster_remap(request, inst->cluster, conn);
	fr_pool_connection_release(pool, request, conn);

	MEM(vb = fr_value_box_alloc_null(ctx));
	fr_value_box_strdup(vb, vb, NULL, fr_int2str(fr_redis_cluster_rcodes_table, rcode, "<INVALID>"), false);
	fr_cursor_append(out, vb);

	return XLAT_ACTION_DONE;
}
/**
 *
 * FIXME do something with mandatory
 */
ssize_t eap_fast_decode_pair(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_attr_t const *parent,
			     uint8_t const *data, size_t data_len,
			     void *decoder_ctx)
{
	fr_dict_attr_t const	*da;
	uint8_t	const		*p = data, *end = p + data_len;

	/*
	 *	Decode the TLVs
	 */
	while (p < end) {
		ssize_t		ret;
		uint16_t	attr;
		uint16_t	len;
		VALUE_PAIR	*vp;

		attr = fr_ntoh16_bin(p) & EAP_FAST_TLV_TYPE;
		p += 2;
		len = fr_ntoh16_bin(p);
		p += 2;

		da = fr_dict_attr_child_by_num(parent, attr);
		if (!da) {
			MEM(vp = fr_pair_afrom_child_num(ctx, parent, attr));

		} else if (da->type == FR_TYPE_TLV) {
			p += (size_t) eap_fast_decode_pair(ctx, cursor, parent, p, len, decoder_ctx);
			continue;

		} else {
			MEM(vp = fr_pair_afrom_da(ctx, da));
		}

		ret = fr_value_box_from_network(vp, &vp->data, vp->vp_type, vp->da, p, len, true);
		if (ret != len) {
			fr_pair_to_unknown(vp);
			fr_pair_value_memcpy(vp, p, len, true);
		}
		fr_cursor_append(cursor, vp);
		p += len;
	}

	return p - data;
}
/** Add our logging destination to the linked list of logging destinations (if it doesn't already exist)
 *
 * @param[in] instance	of rlm_logtee.
 * @param[in] thread	Thread specific data.
 * @param[in] request	request to add our log destination to.
 * @return
 *	- #RLM_MODULE_NOOP	if log destination already exists.
 *	- #RLM_MODULE_OK	if we added a new destination.
 */
static rlm_rcode_t mod_insert_logtee(UNUSED void *instance, void *thread, REQUEST *request)
{
	fr_cursor_t	cursor;
	log_dst_t	*dst;
	bool		exists = false;

	for (dst = fr_cursor_init(&cursor, &request->log.dst); dst; dst = fr_cursor_next(&cursor)) {
		if (dst->uctx == thread) exists = true;
	}

	if (exists) return RLM_MODULE_NOOP;

	dst = talloc_zero(request, log_dst_t);
	dst->func = logtee_it;
	dst->uctx = thread;

	fr_cursor_append(&cursor, dst);

	return RLM_MODULE_OK;
}
Beispiel #9
0
static inline VALUE_PAIR *tls_session_cert_attr_add(TALLOC_CTX *ctx, REQUEST *request, fr_cursor_t *cursor,
					    	    int attr, int attr_index, char const *value)
{
	VALUE_PAIR *vp;
	fr_dict_attr_t const *da = *(cert_attr_names[attr][attr_index]);

	MEM(vp = fr_pair_afrom_da(ctx, da));
	if (value) {
		if (fr_pair_value_from_str(vp, value, -1, '\0', true) < 0) {
			RPWDEBUG("Failed creating attribute %s", da->name);
			talloc_free(vp);
			return NULL;
		}
	}
	RINDENT();
	RDEBUG3("%pP", vp);
	REXDENT();
	fr_cursor_append(cursor, vp);

	return vp;
}
Beispiel #10
0
/** Return the node that is currently servicing a particular key
 *
 *
 */
static xlat_action_t redis_node_xlat(TALLOC_CTX *ctx, fr_cursor_t *out,
				     REQUEST *request, void const *xlat_inst,
				     UNUSED void *xlat_thread_inst,
				     fr_value_box_t **in)
{
	rlm_redis_t const			*inst = talloc_get_type_abort_const(*((void const * const *)xlat_inst),
										    rlm_redis_t);

	fr_redis_cluster_key_slot_t const	*key_slot;
	fr_redis_cluster_node_t const		*node;
	fr_ipaddr_t				ipaddr;
	uint16_t				port;

	char const				*p;
	char					*q;
	char const				*key;
	size_t					key_len;
	unsigned long				idx = 0;
	fr_value_box_t				*vb;

	if (!in) {
		REDEBUG("Missing key");
		return XLAT_ACTION_FAIL;
	}

	if (fr_value_box_list_concat(ctx, *in, in, FR_TYPE_STRING, true) < 0) {
		RPEDEBUG("Failed concatenating input");
		return XLAT_ACTION_FAIL;
	}

	key = p = (*in)->vb_strvalue;
	p = strchr(p, ' ');		/* Look for index */
	if (p) {
		key_len = p - key;

		idx = strtoul(p, &q, 10);
		if (q == p) {
			REDEBUG("Tailing garbage after node index");
			return XLAT_ACTION_FAIL;
		}
	} else {
		key_len = (*in)->vb_length;
	}

	key_slot = fr_redis_cluster_slot_by_key(inst->cluster, request, (uint8_t const *)key, key_len);
	if (idx == 0) {
		node = fr_redis_cluster_master(inst->cluster, key_slot);
	} else {
		node = fr_redis_cluster_slave(inst->cluster, key_slot, idx - 1);
	}

	if (!node) {
		RDEBUG2("No node available for this key slot");
		return XLAT_ACTION_DONE;
	}

	if ((fr_redis_cluster_ipaddr(&ipaddr, node) < 0) || (fr_redis_cluster_port(&port, node) < 0)) {
		REDEBUG("Failed retrieving node information");
		return XLAT_ACTION_FAIL;
	}

	MEM(vb = fr_value_box_alloc_null(ctx));
	fr_value_box_asprintf(vb, vb, NULL, false, "%pV:%u", fr_box_ipaddr(ipaddr), port);
	fr_cursor_append(out, vb);

	return XLAT_ACTION_DONE;
}
Beispiel #11
0
static int radsnmp_send_recv(radsnmp_conf_t *conf, int fd)
{
	fr_strerror();

#define NEXT_LINE(_line, _buffer) \
{ \
	size_t _len; \
	if (stop) return 0; \
	errno = 0;\
	_line = fgets(_buffer, sizeof(_buffer), stdin); \
	if (_line) { \
		_len = strlen(_line); \
		if ((_len > 0) && (_line[_len - 1] == '\n')) _line[_len - 1] = '\0'; \
		DEBUG2("read: %s", _line); \
	} \
}
	/*
	 *	Read commands from pass_persist
	 */
	while (!stop) {
		radsnmp_command_t	command;

		char			buffer[256];
		char			*line;
		ssize_t			slen;

		fr_cursor_t		cursor;
		VALUE_PAIR		*vp;
		RADIUS_PACKET		*request;

		/*
		 *	Alloc a new request so we can start adding
		 *	new pairs to it.
		 */
		request = radsnmp_alloc(conf, fd);
		if (!request) {
			ERROR("Failed allocating request");
			return EXIT_FAILURE;
		}
		fr_cursor_init(&cursor, &request->vps);

		NEXT_LINE(line, buffer);

		/*
		 *	Determine the type of SNMP operation
		 */
		command = fr_str2int(radsnmp_command_str, line, RADSNMP_UNKNOWN);
		switch (command) {
		case RADSNMP_EXIT:
			DEBUG("Empty command, exiting");
			return 0;

		case RADSNMP_PING:
			RESPOND_STATIC("PONG");
			continue;

		case RADSNMP_SET:
		{
			char		value_buff[254];	/* RADIUS attribute length + 1 */
			char		*value;
			char		type_str[64];
			char		*p;
			fr_dict_enum_t	*type;

			NEXT_LINE(line, buffer);	/* Should be the OID */
			NEXT_LINE(value, value_buff);	/* Should be the value */

			p = strchr(value, ' ');
			if (!p) {
				ERROR("No SNMP type specified (or type/value string was malformed)");
				RESPOND_STATIC("NONE");
				continue;
			}

			if ((size_t)(p - value) >= sizeof(type_str)) {
				ERROR("SNMP Type string too long");
				RESPOND_STATIC("NONE");
				continue;
			}

			strlcpy(type_str, value, (p - value) + 1);

			type = fr_dict_enum_by_alias(attr_freeradius_snmp_type, type_str, -1);
			if (!type) {
				ERROR("Unknown type \"%s\"", type_str);
				RESPOND_STATIC("NONE");
				continue;
			}

			slen = radsnmp_pair_from_oid(conf, conf, &cursor, line, type->value->vb_uint32, p + 1);
		}
			break;

		case RADSNMP_GET:
		case RADSNMP_GETNEXT:
			NEXT_LINE(line, buffer);	/* Should be the OID */
			slen = radsnmp_pair_from_oid(conf, conf, &cursor, line, 0, NULL);
			break;

		default:
			ERROR("Unknown command \"%s\"", line);
			RESPOND_STATIC("NONE");
			talloc_free(request);
			continue;
		}

		/*
		 *	Deal with any errors from the GET/GETNEXT/SET command
		 */
		if (slen <= 0) {
			char *spaces, *text;

			fr_canonicalize_error(conf, &spaces, &text, slen, line);

			ERROR("Failed evaluating OID:");
			ERROR("%s", text);
			ERROR("%s^ %s", spaces, fr_strerror());

			talloc_free(spaces);
			talloc_free(text);
			talloc_free(request);
			RESPOND_STATIC("NONE");
			continue;
		}

		/*
		 *	Now add an attribute indicating what the
		 *	SNMP operation was
		 */
		vp = fr_pair_afrom_da(request, attr_freeradius_snmp_operation);
		if (!vp) {
			ERROR("Failed allocating SNMP operation attribute");
			return EXIT_FAILURE;
		}
		vp->vp_uint32 = (unsigned int)command;	/* Commands must match dictionary */
		fr_cursor_append(&cursor, vp);

		/*
		 *	Add message authenticator or the stats
		 *	request will be rejected.
		 */
		MEM(vp = fr_pair_afrom_da(request, attr_message_authenticator));
		fr_pair_value_memcpy(vp, (uint8_t const *)"\0", 1, true);
		fr_cursor_append(&cursor, vp);

		/*
		 *	Send the packet
		 */
		{
			RADIUS_PACKET	*reply = NULL;
			ssize_t		rcode;

			fd_set		set;

			unsigned int	ret;
			unsigned int	i;

			if (fr_radius_packet_encode(request, NULL, conf->secret) < 0) {
				ERROR("Failed encoding request: %s", fr_strerror());
				return EXIT_FAILURE;
			}
			if (fr_radius_packet_sign(request, NULL, conf->secret) < 0) {
				ERROR("Failed signing request: %s", fr_strerror());
				return EXIT_FAILURE;
			}

			/*
			 *	Print the attributes we're about to send
			 */
			if (fr_log_fp) fr_packet_header_print(fr_log_fp, request, false);
			if (fr_debug_lvl > 0) fr_pair_list_fprint(fr_log_fp, request->vps);
#ifndef NDEBUG
			if (fr_log_fp && (fr_debug_lvl > 3)) fr_radius_packet_print_hex(request);
#endif

			FD_ZERO(&set); /* clear the set */
			FD_SET(fd, &set);

			/*
			 *	Any connection issues cause us to exit, so
			 *	the connection can be re-initialised on the
			 *	next call.
			 */
			for (i = 0; i < conf->retries; i++) {
				rcode = write(request->sockfd, request->data, request->data_len);
				if (rcode < 0) {
					ERROR("Failed sending: %s", fr_syserror(errno));
					return EXIT_FAILURE;
				}

				rcode = select(fd + 1, &set, NULL, NULL, &conf->timeout);
				switch (rcode) {
				case -1:
					ERROR("Select failed: %s", fr_syserror(errno));
					return EXIT_FAILURE;

				case 0:
					DEBUG("Response timeout.  Retrying %d/%u...", i + 1, conf->retries);
					continue;	/* Timeout */

				case 1:
					reply = fr_radius_packet_recv(request, request->sockfd, UDP_FLAGS_NONE,
								      RADIUS_MAX_ATTRIBUTES, false);
					if (!reply) {
						ERROR("Failed receiving reply: %s", fr_strerror());
					recv_error:
						RESPOND_STATIC("NONE");
						talloc_free(request);
						continue;
					}
					if (fr_radius_packet_decode(reply, request,
								    RADIUS_MAX_ATTRIBUTES, false, conf->secret) < 0) {
						ERROR("Failed decoding reply: %s", fr_strerror());
						goto recv_error;
					}
					break;

				default:
					DEBUG("Invalid select() return value %zd", rcode);
					return EXIT_FAILURE;
				}
				break;
			}

			if (!reply) {
				ERROR("Server didn't respond");
				return EXIT_FAILURE;
			}

			/*
			 *	Print the attributes we received in response
			 */
			if (fr_log_fp) fr_packet_header_print(fr_log_fp, reply, true);
			if (fr_debug_lvl > 0) fr_pair_list_fprint(fr_log_fp, reply->vps);
#ifndef NDEBUG
			if (fr_log_fp && (fr_debug_lvl > 3)) fr_radius_packet_print_hex(reply);
#endif

			switch (command) {
			case RADSNMP_GET:
			case RADSNMP_GETNEXT:
				ret = radsnmp_get_response(STDOUT_FILENO, conf->snmp_oid_root,
							   attr_freeradius_snmp_type, reply->vps);
				switch (ret) {
				case -1:
					ERROR("Failed converting pairs to varbind response: %s", fr_strerror());
					return EXIT_FAILURE;

				case 0:
					DEBUG("Empty response");
					break;

				default:
					DEBUG("Returned %u varbind responses", ret);
					break;
				}
				break;

			case RADSNMP_SET:
				if (radsnmp_set_response(STDOUT_FILENO, attr_freeradius_snmp_failure, reply->vps) < 0) {
					ERROR("Failed writing SET response: %s", fr_strerror());
					return EXIT_FAILURE;
				}
				break;

			default:
				assert(0);
				return EXIT_FAILURE;
			}


			talloc_free(request);
		}
	}

	return EXIT_SUCCESS;
}
Beispiel #12
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;
}
Beispiel #13
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;
}
Beispiel #14
0
/** 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;
}
Beispiel #15
0
/*
 *	Do the statistics
 */
static rlm_rcode_t CC_HINT(nonnull) mod_stats(void *instance, void *thread, REQUEST *request)
{
	int i;
	uint32_t stats_type;
	rlm_stats_thread_t *t = thread;
	rlm_stats_t *inst = instance;
	VALUE_PAIR *vp;
	rlm_stats_data_t mydata, *stats;
	fr_cursor_t cursor;
	char buffer[64];
	uint64_t local_stats[sizeof(inst->stats) / sizeof(inst->stats[0])];

	/*
	 *	Increment counters only in "send foo" sections.
	 *
	 *	i.e. only when we have a reply to send.
	 */
	if (request->request_state == REQUEST_SEND) {
		int src_code, dst_code;

		src_code = request->packet->code;
		if (src_code >= FR_MAX_PACKET_CODE) src_code = 0;

		dst_code = request->reply->code;
		if (dst_code >= FR_MAX_PACKET_CODE) dst_code = 0;

		t->stats[src_code]++;
		t->stats[dst_code]++;

		/*
		 *	Update source statistics
		 */
		mydata.ipaddr = request->packet->src_ipaddr;
		stats = rbtree_finddata(t->src, &mydata);
		if (!stats) {
			MEM(stats = talloc_zero(t, rlm_stats_data_t));

			stats->ipaddr = request->packet->src_ipaddr;
			stats->created = request->async->recv_time;

			(void) rbtree_insert(t->src, stats);
		}

		stats->last_packet = request->async->recv_time;
		stats->stats[src_code]++;
		stats->stats[dst_code]++;

		/*
		 *	Update destination statistics
		 */
		mydata.ipaddr = request->packet->dst_ipaddr;
		stats = rbtree_finddata(t->dst, &mydata);
		if (!stats) {
			MEM(stats = talloc_zero(t, rlm_stats_data_t));

			stats->ipaddr = request->packet->dst_ipaddr;
			stats->created = request->async->recv_time;

			(void) rbtree_insert(t->dst, stats);
		}

		stats->last_packet = request->async->recv_time;
		stats->stats[src_code]++;
		stats->stats[dst_code]++;

		/*
		 *	@todo - periodically clean up old entries.
		 */

		if ((t->last_global_update + NANOSEC) > request->async->recv_time) {
			return RLM_MODULE_UPDATED;
		}

		t->last_global_update = request->async->recv_time;

		pthread_mutex_lock(&inst->mutex);
		for (i = 0; i < FR_MAX_PACKET_CODE; i++) {
			inst->stats[i] += t->stats[i];
			t->stats[i] = 0;
		}
		pthread_mutex_unlock(&inst->mutex);

		return RLM_MODULE_UPDATED;
	}

	/*
	 *	Ignore "authenticate" and anything other than Status-Server
	 */
	if ((request->request_state != REQUEST_RECV) ||
	    (request->packet->code != FR_CODE_STATUS_SERVER)) {
		return RLM_MODULE_NOOP;
	}

	vp = fr_pair_find_by_da(request->packet->vps, attr_freeradius_stats4_type, TAG_ANY);
	if (!vp) {
		stats_type = FR_FREERADIUS_STATS4_TYPE_VALUE_GLOBAL;
	} else {
		stats_type = vp->vp_uint32;
	}

	/*
	 *	Create attributes based on the statistics.
	 */
	fr_cursor_init(&cursor, &request->reply->vps);

	MEM(pair_update_reply(&vp, attr_freeradius_stats4_type) >= 0);
	vp->vp_uint32 = stats_type;

	switch (stats_type) {
	case FR_FREERADIUS_STATS4_TYPE_VALUE_GLOBAL:			/* global */
		/*
		 *	Merge our stats with the global stats, and then copy
		 *	the global stats to a thread-local variable.
		 *
		 *	The copy helps minimize mutex contention.
		 */
		pthread_mutex_lock(&inst->mutex);
		for (i = 0; i < FR_MAX_PACKET_CODE; i++) {
			inst->stats[i] += t->stats[i];
			t->stats[i] = 0;
		}
		memcpy(&local_stats, inst->stats, sizeof(inst->stats));
		pthread_mutex_unlock(&inst->mutex);
		vp = NULL;
		break;

	case FR_FREERADIUS_STATS4_TYPE_VALUE_CLIENT:			/* src */
		vp = fr_pair_find_by_da(request->packet->vps, attr_freeradius_stats4_ipv4_address, TAG_ANY);
		if (!vp) vp = fr_pair_find_by_da(request->packet->vps, attr_freeradius_stats4_ipv6_address, TAG_ANY);
		if (!vp) return RLM_MODULE_NOOP;

		mydata.ipaddr = vp->vp_ip;
		coalesce(local_stats, t, offsetof(rlm_stats_thread_t, src), &mydata);
		break;

	case FR_FREERADIUS_STATS4_TYPE_VALUE_LISTENER:			/* dst */
		vp = fr_pair_find_by_da(request->packet->vps, attr_freeradius_stats4_ipv4_address, TAG_ANY);
		if (!vp) vp = fr_pair_find_by_da(request->packet->vps, attr_freeradius_stats4_ipv6_address, TAG_ANY);
		if (!vp) return RLM_MODULE_NOOP;

		mydata.ipaddr = vp->vp_ip;
		coalesce(local_stats, t, offsetof(rlm_stats_thread_t, dst), &mydata);
		break;

	default:
		REDEBUG("Invalid value '%d' for FreeRADIUS-Stats4-type", stats_type);
		return RLM_MODULE_FAIL;
	}

	if (vp ) {
		vp = fr_pair_copy(request->reply, vp);
		if (vp) {
			fr_cursor_append(&cursor, vp);
			(void) fr_cursor_tail(&cursor);
		}
	}

	strcpy(buffer, "FreeRADIUS-Stats4-");

	for (i = 0; i < FR_MAX_PACKET_CODE; i++) {
		fr_dict_attr_t const *da;

		if (!local_stats[i]) continue;

		strlcpy(buffer + 18, fr_packet_codes[i], sizeof(buffer) - 18);
		da = fr_dict_attr_by_name(dict_radius, buffer);
		if (!da) continue;

		vp = fr_pair_afrom_da(request->reply, da);
		if (!vp) return RLM_MODULE_FAIL;

		vp->vp_uint64 = local_stats[i];

		fr_cursor_append(&cursor, vp);
		(void) fr_cursor_tail(&cursor);
	}

	return RLM_MODULE_OK;
}
Beispiel #16
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;
}
Beispiel #17
0
/** Extract attributes from an X509 certificate
 *
 * @param cursor	to copy attributes to.
 * @param ctx		to allocate attributes in.
 * @param session	current TLS session.
 * @param cert		to validate.
 * @param depth		the certificate is in the certificate chain (0 == leaf).
 * @return
 *	- 0 on success.
 *	- < 0 on failure.
 */
int tls_session_pairs_from_x509_cert(fr_cursor_t *cursor, TALLOC_CTX *ctx,
				     tls_session_t *session, X509 *cert, int depth)
{
	char		buffer[1024];
	char		attribute[256];
	char		**identity;
	int		attr_index, loc;

#if OPENSSL_VERSION_NUMBER >= 0x10100000L
	STACK_OF(X509_EXTENSION) const *ext_list = NULL;
#else
	STACK_OF(X509_EXTENSION) *ext_list = NULL;
#endif

	ASN1_INTEGER	*sn = NULL;
	ASN1_TIME	*asn_time = NULL;

	VALUE_PAIR	*vp = NULL;

	REQUEST		*request;

#define CERT_ATTR_ADD(_attr, _attr_index, _value) tls_session_cert_attr_add(ctx, request, cursor, _attr, _attr_index, _value)

	attr_index = depth;
	if (attr_index > 1) attr_index = 1;

	request = (REQUEST *)SSL_get_ex_data(session->ssl, FR_TLS_EX_INDEX_REQUEST);
	rad_assert(request != NULL);

	identity = (char **)SSL_get_ex_data(session->ssl, FR_TLS_EX_INDEX_IDENTITY);

	if (RDEBUG_ENABLED3) {
		buffer[0] = '\0';
		X509_NAME_oneline(X509_get_subject_name(cert), buffer, sizeof(buffer));
		buffer[sizeof(buffer) - 1] = '\0';
		RDEBUG3("Creating attributes for \"%s\":", buffer[0] ? buffer : "Cert missing subject OID");
	}

	/*
	 *	Get the Serial Number
	 */
	sn = X509_get_serialNumber(cert);
	if (sn && ((size_t) sn->length < (sizeof(buffer) / 2))) {
		char *p = buffer;
		int i;

		for (i = 0; i < sn->length; i++) {
			sprintf(p, "%02x", (unsigned int)sn->data[i]);
			p += 2;
		}

		CERT_ATTR_ADD(IDX_SERIAL, attr_index, buffer);
	}

	/*
	 *	Get the Expiration Date
	 */
	buffer[0] = '\0';
	asn_time = X509_get_notAfter(cert);
	if (identity && asn_time && (asn_time->length < (int)sizeof(buffer))) {
		time_t expires;

		/*
		 *	Add expiration as a time since the epoch
		 */
		if (tls_utils_asn1time_to_epoch(&expires, asn_time) < 0) {
			RPWDEBUG("Failed parsing certificate expiry time");
		} else {
			vp = CERT_ATTR_ADD(IDX_EXPIRATION, attr_index, NULL);
			vp->vp_date = expires;
		}
	}

	/*
	 *	Get the Subject & Issuer
	 */
	buffer[0] = '\0';
	X509_NAME_oneline(X509_get_subject_name(cert), buffer, sizeof(buffer));
	buffer[sizeof(buffer) - 1] = '\0';
	if (identity && buffer[0]) {
		CERT_ATTR_ADD(IDX_SUBJECT, attr_index, buffer);

		/*
		 *	Get the Common Name, if there is a subject.
		 */
		X509_NAME_get_text_by_NID(X509_get_subject_name(cert),
					  NID_commonName, buffer, sizeof(buffer));
		buffer[sizeof(buffer) - 1] = '\0';

		if (buffer[0]) {
			CERT_ATTR_ADD(IDX_COMMON_NAME, attr_index, buffer);
		}
	}

	X509_NAME_oneline(X509_get_issuer_name(cert), buffer, sizeof(buffer));
	buffer[sizeof(buffer) - 1] = '\0';
	if (identity && buffer[0]) {
		CERT_ATTR_ADD(IDX_ISSUER, attr_index, buffer);
	}

	/*
	 *	Get the RFC822 Subject Alternative Name
	 */
	loc = X509_get_ext_by_NID(cert, NID_subject_alt_name, 0);
	if (loc >= 0) {
		X509_EXTENSION	*ext = NULL;
		GENERAL_NAMES	*names = NULL;
		int		i;

		ext = X509_get_ext(cert, loc);
		if (ext && (names = X509V3_EXT_d2i(ext))) {
			for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
				GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);

				switch (name->type) {
#ifdef GEN_EMAIL
				case GEN_EMAIL: {
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
					char const *rfc822Name = (char const *)ASN1_STRING_get0_data(name->d.rfc822Name);
#else
					char *rfc822Name = (char *)ASN1_STRING_data(name->d.rfc822Name);
#endif

					CERT_ATTR_ADD(IDX_SUBJECT_ALT_NAME_EMAIL, attr_index, rfc822Name);
					break;
				}
#endif	/* GEN_EMAIL */
#ifdef GEN_DNS
				case GEN_DNS: {
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
					char const *dNSName = (char const *)ASN1_STRING_get0_data(name->d.dNSName);
#else
					char *dNSName = (char *)ASN1_STRING_data(name->d.dNSName);
#endif
					CERT_ATTR_ADD(IDX_SUBJECT_ALT_NAME_DNS, attr_index, dNSName);
					break;
				}
#endif	/* GEN_DNS */
#ifdef GEN_OTHERNAME
				case GEN_OTHERNAME:
					/* look for a MS UPN */
					if (NID_ms_upn != OBJ_obj2nid(name->d.otherName->type_id)) break;

					/* we've got a UPN - Must be ASN1-encoded UTF8 string */
					if (name->d.otherName->value->type == V_ASN1_UTF8STRING) {
						CERT_ATTR_ADD(IDX_SUBJECT_ALT_NAME_UPN, attr_index,
								  (char *)name->d.otherName->value->value.utf8string);
						break;
					}

					RWARN("Invalid UPN in Subject Alt Name (should be UTF-8)");
					break;
#endif	/* GEN_OTHERNAME */
				default:
					/* XXX TODO handle other SAN types */
					break;
				}
			}
		}
		if (names != NULL) GENERAL_NAMES_free(names);
	}

	/*
	 *	Only add extensions for the actual client certificate
	 */
	if (attr_index == 0) {
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
		ext_list = X509_get0_extensions(cert);
#else
		ext_list = cert->cert_info->extensions;
#endif

		/*
		 *	Grab the X509 extensions, and create attributes out of them.
		 *	For laziness, we re-use the OpenSSL names
		 */
		if (sk_X509_EXTENSION_num(ext_list) > 0) {
			int i, len;
			char *p;
			BIO *out;

			out = BIO_new(BIO_s_mem());
			strlcpy(attribute, "TLS-Client-Cert-", sizeof(attribute));

			for (i = 0; i < sk_X509_EXTENSION_num(ext_list); i++) {
				char			value[1024];
				ASN1_OBJECT		*obj;
				X509_EXTENSION		*ext;
				fr_dict_attr_t const	*da;

				ext = sk_X509_EXTENSION_value(ext_list, i);

				obj = X509_EXTENSION_get_object(ext);
				i2a_ASN1_OBJECT(out, obj);

				len = BIO_read(out, attribute + 16 , sizeof(attribute) - 16 - 1);
				if (len <= 0) continue;

				attribute[16 + len] = '\0';

				for (p = attribute + 16; *p != '\0'; p++) if (*p == ' ') *p = '-';

				X509V3_EXT_print(out, ext, 0, 0);
				len = BIO_read(out, value , sizeof(value) - 1);
				if (len <= 0) continue;

				value[len] = '\0';

				da = fr_dict_attr_by_name(dict_freeradius, attribute);
				if (!da) {
					RWDEBUG3("Skipping attribute %s: "
						 "Add dictionary definition if you want to access it", attribute);
					continue;
				}

				MEM(vp = fr_pair_afrom_da(request, da));
				if (fr_pair_value_from_str(vp, value, -1, '\0', true) < 0) {
					RPWDEBUG3("Skipping: %s += '%s'", attribute, value);
					talloc_free(vp);
					continue;
				}

				fr_cursor_append(cursor, vp);
			}
			BIO_free_all(out);
		}
	}

	return 0;
}
Beispiel #18
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;
}
/** Logging callback to write log messages to a destination
 *
 * This allows the logging destination to be customised on a per request basis.
 *
 * @note Function does not write log output immediately
 *
 * @param[in] type	What type of message this is (error, warn, info, debug).
 * @param[in] lvl	At what logging level this message should be output.
 * @param[in] request	The current request.
 * @param[in] file	src file the log message was generated in.
 * @param[in] line	number the log message was generated on.
 * @param[in] fmt	sprintf style fmt string.
 * @param[in] ap	Arguments for the fmt string.
 * @param[in] uctx	Context data for the log function.
 */
static void logtee_it(fr_log_type_t type, fr_log_lvl_t lvl, REQUEST *request,
		      UNUSED char const *file, UNUSED int line,
		      char const *fmt, va_list ap, void *uctx)
{
	rlm_logtee_thread_t	*t = talloc_get_type_abort(uctx, rlm_logtee_thread_t);
	rlm_logtee_t const	*inst = t->inst;
	char			*msg, *exp;
	fr_cursor_t		cursor;
	VALUE_PAIR		*vp;
	log_dst_t		*dst;

	rad_assert(t->msg->vp_length == 0);	/* Should have been cleared before returning */

	/*
	 *	None of this should involve mallocs unless msg > 1k
	 */
	msg = talloc_typed_vasprintf(t->msg, fmt, ap);
	fr_value_box_strdup_buffer_shallow(NULL, &t->msg->data, attr_log_message, msg, true);

	t->type->vp_uint32 = (uint32_t) type;
	t->lvl->vp_uint32 = (uint32_t) lvl;

	fr_cursor_init(&cursor, &request->packet->vps);
	fr_cursor_prepend(&cursor, t->msg);
	fr_cursor_prepend(&cursor, t->type);
	fr_cursor_prepend(&cursor, t->lvl);
	fr_cursor_head(&cursor);

	/*
	 *	Now expand our fmt string to encapsulate the
	 *	message and any metadata
	 *
	 *	Fixme: Would be better to call tmpl_expand
	 *	into a variable length ring buffer.
	 */
	dst = request->log.dst;
	request->log.dst = NULL;
	if (tmpl_aexpand(t, &exp, request, inst->log_fmt, NULL, NULL) < 0) goto finish;
	request->log.dst = dst;

	fr_fring_overwrite(t->fring, exp);	/* Insert it into the buffer */

	if (!t->pending) {
		t->pending = true;
		logtee_fd_active(t);		/* Listen for when the fd is writable */
	}

finish:
	/*
	 *	Don't free, we re-use the VALUE_PAIRs for the next message
	 */
	vp = fr_cursor_remove(&cursor);
	if (!fr_cond_assert(vp == t->lvl)) fr_cursor_append(&cursor, vp);

	vp = fr_cursor_remove(&cursor);
	if (!fr_cond_assert(vp == t->type)) fr_cursor_append(&cursor, vp);

	vp = fr_cursor_remove(&cursor);
	if (!fr_cond_assert(vp == t->msg)) fr_cursor_append(&cursor, vp);

	fr_value_box_clear(&t->msg->data);		/* Clear message data */
}
Beispiel #20
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;
}