Beispiel #1
0
static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, UNUSED void *thread, REQUEST *request)
{
	rlm_chap_t	*inst = instance;
	VALUE_PAIR	*vp;

	if (!fr_pair_find_by_da(request->packet->vps, attr_chap_password, TAG_ANY)) return RLM_MODULE_NOOP;

	/*
	 *	Create the CHAP-Challenge if it wasn't already in the packet.
	 *
	 *	This is so that the rest of the code does not need to
	 *	understand CHAP.
	 */

	vp = fr_pair_find_by_da(request->packet->vps, attr_chap_challenge, TAG_ANY);
	if (!vp) {
		RDEBUG2("Creating CHAP-Challenge from the request authenticator");

		MEM(vp = fr_pair_afrom_da(request->packet, attr_chap_challenge));
		fr_pair_value_memcpy(vp, request->packet->vector, sizeof(request->packet->vector));
		fr_pair_add(&request->packet->vps, vp);
	}

	if (!module_section_type_set(request, attr_auth_type, inst->auth_type)) return RLM_MODULE_NOOP;

	return RLM_MODULE_OK;
}
Beispiel #2
0
static int arp_socket_decode(UNUSED rad_listen_t *listener, REQUEST *request)
{
	int			i;
	uint8_t	const		*p = request->packet->data, *end = p + request->packet->data_len;
	fr_cursor_t		cursor;

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

	for (i = 0; header_names[i].name != NULL; i++) {
		ssize_t			ret;
		size_t			len;
		fr_dict_attr_t const	*da;
		VALUE_PAIR		*vp = NULL;

		len = header_names[i].len;

		if (!fr_cond_assert((size_t)(end - p) < len)) return -1; /* Should have been detected in socket_recv */

		da = fr_dict_attr_by_name(dict_arp, header_names[i].name);
		if (!da) return 0;

		MEM(vp = fr_pair_afrom_da(request->packet, da));
		ret = fr_value_box_from_network(vp, &vp->data, da->type, da, p, len, true);
		if (ret <= 0) {
			fr_pair_to_unknown(vp);
			fr_pair_value_memcpy(vp, p, len);
		}

		DEBUG2("&%pP", vp);
		fr_cursor_insert(&cursor, vp);
	}

	return 0;
}
/** Do any RADIUS-layer fixups for proxying.
 *
 */
static void radius_fixups(rlm_radius_t *inst, REQUEST *request)
{
	VALUE_PAIR *vp;

	/*
	 *	Check for proxy loops.
	 */
	if (RDEBUG_ENABLED) {
		fr_cursor_t cursor;

		for (vp = fr_cursor_iter_by_da_init(&cursor, &request->packet->vps, attr_proxy_state);
		     vp;
		     vp = fr_cursor_next(&cursor)) {
			if (vp->vp_length != 4) continue;

			if (memcmp(&inst->proxy_state, vp->vp_octets, 4) == 0) {
				RWARN("Possible proxy loop - please check server configuration.");
				break;
			}
		}
	}

	if (request->packet->code != FR_CODE_ACCESS_REQUEST) return;

	if (fr_pair_find_by_da(request->packet->vps, attr_chap_password, TAG_ANY) &&
	    !fr_pair_find_by_da(request->packet->vps, attr_chap_challenge, TAG_ANY)) {
	    	MEM(pair_add_request(&vp, attr_chap_challenge) >= 0);
		fr_pair_value_memcpy(vp, request->packet->vector, sizeof(request->packet->vector));
	}
}
/*
 *	Add raw hex data to the reply.
 */
void eap_add_reply(REQUEST *request,
		   char const *name, uint8_t const *value, int len)
{
	VALUE_PAIR *vp;

	vp = pair_make_reply(name, NULL, T_OP_EQ);
	if (!vp) {
		REDEBUG("Did not create attribute %s: %s\n",
			name, fr_strerror());
		return;
	}

	fr_pair_value_memcpy(vp, value, len);
}
/** Process an entry modification operation
 *
 * @note This is a callback for the sync_demux function.
 *
 * @param[in] conn	the sync belongs to.
 * @param[in] config	of the sync that received an entry.
 * @param[in] sync_id	of the sync that received an entry.
 * @param[in] phase	Refresh phase the sync is currently in.
 * @param[in] uuid	of the entry.
 * @param[in] msg	containing the entry.
 * @param[in] state	The type of modification we need to perform to our
 *			representation of the entry.
 * @param[in] user_ctx	The listener.
 * @return
 *	- 0 on success.
 *	- -1 on failure.
 */
static int _proto_ldap_entry(fr_ldap_connection_t *conn, sync_config_t const *config,
			     int sync_id, UNUSED sync_phases_t phase,
			     uint8_t const uuid[SYNC_UUID_LENGTH], LDAPMessage *msg,
			     sync_states_t state, void *user_ctx)
{
	rad_listen_t		*listen = talloc_get_type_abort(user_ctx, rad_listen_t);
	proto_ldap_inst_t	*inst = talloc_get_type_abort(listen->data, proto_ldap_inst_t);
	fr_ldap_map_exp_t	expanded;
	REQUEST			*request;

	request = proto_ldap_request_setup(listen, inst, sync_id);
	if (!request) return -1;

	proto_ldap_attributes_add(request, config);
	request->packet->code = state;

	/*
	 *	Add the entry DN and attributes
	 */
	if (msg) {
		char *entry_dn;
		VALUE_PAIR *vp;

		entry_dn = ldap_get_dn(conn->handle, msg);

		MEM(pair_update_request(&vp, attr_ldap_sync_entry_dn) >= 0);
		ldap_memfree(entry_dn);

		MEM(pair_update_request(&vp, attr_ldap_sync_entry_uuid) >= 0);
		fr_pair_value_memcpy(vp, uuid, SYNC_UUID_LENGTH);
	}

	/*
	 *	Apply the attribute map
	 */
	if (fr_ldap_map_expand(&expanded, request, config->entry_map) < 0) {
	error:
		talloc_free(request);
		return -1;
	}
	if (fr_ldap_map_do(request, conn, NULL, &expanded, msg) < 0) goto error;

//	request_enqueue(request);

	return 0;
}
/**
 *
 * 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;
}
Beispiel #7
0
/*
 *	Access-Requests can have the CHAP-Challenge implicitly taken
 *	from the request authenticator.  If the NAS has done that,
 *	then we need to copy the data to a real CHAP-Challenge
 *	attribute when proxying.  Otherwise when we proxy the request,
 *	the new authenticator is different, and the CHAP calculations
 *	will fail.
 */
static rlm_rcode_t CC_HINT(nonnull) mod_pre_proxy(UNUSED void *instance,
				 REQUEST *request)
{
	VALUE_PAIR *vp;

	/*
	 *	For Access-Requests, which have CHAP-Password,
	 *	and no CHAP-Challenge, copy it over from the request.
	 */
	if (request->packet->code != PW_CODE_ACCESS_REQUEST) return RLM_MODULE_NOOP;

	if (!fr_pair_find_by_num(request->proxy->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) return RLM_MODULE_NOOP;

	vp = radius_pair_create(request, &request->proxy->vps, PW_CHAP_CHALLENGE, 0);
	if (!vp) return RLM_MODULE_FAIL;

	fr_pair_value_memcpy(vp, request->packet->vector, sizeof(request->packet->vector));

	return RLM_MODULE_OK;
}
/** Enque a new cookie store request
 *
 * Create a new request containing the cookie we received from the LDAP server. This allows
 * the administrator to store the cookie and provide it on a future call to
 * #proto_ldap_cookie_load.
 *
 * @note This is a callback for the sync_demux function.
 *
 * @param[in] conn	the cookie was received on.
 * @param[in] config	of the LDAP sync.
 * @param[in] sync_id	sync number (msgid) of the sync within the context of the connection.
 * @param[in] cookie	received from the LDAP server.
 * @param[in] user_ctx	listener.
 * @return
 *	- 0 on success.
 *	- -1 on failure
 */
static int _proto_ldap_cookie_store(UNUSED fr_ldap_connection_t *conn, sync_config_t const *config,
			      	    int sync_id, uint8_t const *cookie, void *user_ctx)
{
	rad_listen_t		*listen = talloc_get_type_abort(user_ctx, rad_listen_t);
	proto_ldap_inst_t	*inst = talloc_get_type_abort(listen->data, proto_ldap_inst_t);
	REQUEST			*request;
	VALUE_PAIR		*vp;

	request = proto_ldap_request_setup(listen, inst, sync_id);
	if (!request) return -1;

	proto_ldap_attributes_add(request, config);

	MEM(pair_update_request(&vp, attr_ldap_sync_cookie) >= 0);
	fr_pair_value_memcpy(vp, cookie, talloc_array_length(cookie));

	request->packet->code = LDAP_SYNC_CODE_COOKIE_STORE;

//	request_enqueue(request);

	return 0;
}
Beispiel #9
0
/*
 *
 *     Verify that a Perl SV is a string and save it in FreeRadius
 *     Value Pair Format
 *
 */
static int pairadd_sv(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR **vps, char *key, SV *sv, FR_TOKEN op,
		      const char *hash_name, const char *list_name)
{
	char		*val;
	VALUE_PAIR      *vp;
	STRLEN		len;

	if (!SvOK(sv)) return -1;

	val = SvPV(sv, len);
	vp = fr_pair_make(ctx, request->dict, vps, key, NULL, op);
	if (!vp) {
	fail:
		REDEBUG("Failed to create pair %s:%s %s %s", list_name, key,
			fr_int2str(fr_tokens_table, op, "<INVALID>"), val);
		return -1;
	}

	switch (vp->vp_type) {
	case FR_TYPE_STRING:
		fr_pair_value_bstrncpy(vp, val, len);
		break;

	case FR_TYPE_OCTETS:
		fr_pair_value_memcpy(vp, (uint8_t const *)val, len);
		break;

	default:
		if (fr_pair_value_from_str(vp, val, len, '\0', false) < 0) goto fail;
	}

	VP_VERIFY(vp);

	RDEBUG2("&%s:%s %s $%s{'%s'} -> '%s'", list_name, key, fr_int2str(fr_tokens_table, op, "<INVALID>"),
	        hash_name, key, val);
	return 0;
}
VALUE_PAIR *eap_packet2vp(RADIUS_PACKET *packet, eap_packet_raw_t const *eap)
{
	int		total, size;
	uint8_t const *ptr;
	VALUE_PAIR	*head = NULL;
	VALUE_PAIR	*vp;
	vp_cursor_t	out;

	total = eap->length[0] * 256 + eap->length[1];

	if (total == 0) {
		DEBUG("Asked to encode empty EAP-Message!");
		return NULL;
	}

	ptr = (uint8_t const *) eap;

	fr_cursor_init(&out, &head);
	do {
		size = total;
		if (size > 253) size = 253;

		vp = fr_pair_afrom_num(packet, 0, PW_EAP_MESSAGE);
		if (!vp) {
			fr_pair_list_free(&head);
			return NULL;
		}
		fr_pair_value_memcpy(vp, ptr, size);

		fr_cursor_insert(&out, vp);

		ptr += size;
		total -= size;
	} while (total > 0);

	return head;
}
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
/** 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 #13
0
/** Send the challenge itself
 *
 * Challenges will come from one of three places eventually:
 *
 * 1  from attributes like FR_EAP_AKA_RANDx
 *	    (these might be retrieved from a database)
 *
 * 2  from internally implemented SIM authenticators
 *	    (a simple one based upon XOR will be provided)
 *
 * 3  from some kind of SS7 interface.
 *
 * For now, they only come from attributes.
 * It might be that the best way to do 2/3 will be with a different
 * module to generate/calculate things.
 */
static int eap_aka_send_challenge(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		**to_peer, *vp;
	RADIUS_PACKET		*packet;
	fr_sim_vector_src_t	src = SIM_VECTOR_SRC_AUTO;

	rad_assert(request);
	rad_assert(request->reply);

	/*
	 *	to_peer is the data to the client
	 */
	packet = eap_session->request->reply;
	to_peer = &packet->vps;

	RDEBUG2("Acquiring UMTS vector(s)");

	/*
	 *	Toggle the AMF high bit to indicate we're doing AKA'
	 */
	if (eap_aka_session->type == FR_EAP_AKA_PRIME) {
		uint8_t	amf_buff[2] = { 0x80, 0x00 };	/* Set the AMF separation bit high */

		MEM(pair_update_control(&vp, attr_sim_amf) >= 0);
		fr_pair_value_memcpy(vp, amf_buff, sizeof(amf_buff));
	}

	/*
	 *	Get vectors from attribute or generate
	 *	them using COMP128-* or Milenage.
	 */
	if (fr_sim_vector_umts_from_attrs(eap_session, request->control, &eap_aka_session->keys, &src) != 0) {
	    	REDEBUG("Failed retrieving UMTS vectors");
		return RLM_MODULE_FAIL;
	}

	/*
	 *	Don't leave the AMF hanging around
	 */
	if (eap_aka_session->type == FR_EAP_AKA_PRIME) pair_delete_control(attr_sim_amf);

	/*
	 *	All set, calculate keys!
	 */
	switch (eap_aka_session->kdf) {
	case FR_EAP_AKA_KDF_VALUE_EAP_AKA_PRIME_WITH_CK_PRIME_IK_PRIME:
		fr_sim_crypto_kdf_1_umts(&eap_aka_session->keys);
		break;

	default:
		fr_sim_crypto_kdf_0_umts(&eap_aka_session->keys);
		break;
	}
	if (RDEBUG_ENABLED3) fr_sim_crypto_keys_log(request, &eap_aka_session->keys);

	RDEBUG2("Sending AKA-Challenge");
	eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;

	/*
	 *	Set the subtype to challenge
	 */
	MEM(vp = fr_pair_afrom_da(packet, attr_eap_aka_subtype));
	vp->vp_uint16 = FR_EAP_AKA_SUBTYPE_VALUE_AKA_CHALLENGE;
	fr_pair_replace(to_peer, vp);

	/*
	 *	Indicate we'd like to use protected success messages
	 */
	if (eap_aka_session->send_result_ind) {
		MEM(vp = fr_pair_afrom_da(packet, attr_eap_aka_result_ind));
		vp->vp_bool = true;
		fr_pair_replace(to_peer, vp);
	}

	/*
	 *	We support EAP-AKA' and the peer should use that
	 *	if it's able to...
	 */
	if (eap_aka_session->send_at_bidding) {
		MEM(vp = fr_pair_afrom_da(packet, attr_eap_aka_bidding));
		vp->vp_uint16 = FR_EAP_AKA_BIDDING_VALUE_PREFER_AKA_PRIME;
		fr_pair_replace(to_peer, vp);
	}

	/*
	 *	Send the network name and KDF to the peer
	 */
	if (eap_aka_session->type == FR_EAP_AKA_PRIME) {
		if (!eap_aka_session->keys.network_len) {
			REDEBUG2("No network name available, can't set EAP-AKA-KDF-Input");
		failure:
			fr_pair_list_free(&packet->vps);
			return -1;
		}
		MEM(vp = fr_pair_afrom_da(packet, attr_eap_aka_kdf_input));
		fr_pair_value_bstrncpy(vp, eap_aka_session->keys.network, eap_aka_session->keys.network_len);
		fr_pair_replace(to_peer, vp);

		MEM(vp = fr_pair_afrom_da(packet, attr_eap_aka_kdf));
		vp->vp_uint16 = eap_aka_session->kdf;
		fr_pair_replace(to_peer, vp);
	}

	/*
	 *	Okay, we got the challenge! Put it into an attribute.
	 */
	MEM(vp = fr_pair_afrom_da(packet, attr_eap_aka_rand));
	fr_pair_value_memcpy(vp, eap_aka_session->keys.umts.vector.rand, SIM_VECTOR_UMTS_RAND_SIZE);
	fr_pair_replace(to_peer, vp);

	/*
	 *	Send the AUTN value to the client, so it can authenticate
	 *	whoever has knowledge of the Ki.
	 */
	MEM(vp = fr_pair_afrom_da(packet, attr_eap_aka_autn));
	fr_pair_value_memcpy(vp, eap_aka_session->keys.umts.vector.autn, SIM_VECTOR_UMTS_AUTN_SIZE);
	fr_pair_replace(to_peer, vp);

	/*
	 *	need to include an AT_MAC attribute so that it will get
	 *	calculated.
	 */
	MEM(vp = fr_pair_afrom_da(packet, attr_eap_aka_mac));
	fr_pair_replace(to_peer, vp);

	/*
	 *	If we have checkcode data, send that to the peer
	 *	for validation.
	 */
	if (eap_aka_session->checkcode_state) {
		ssize_t	slen;

		slen = fr_sim_crypto_finalise_checkcode(eap_aka_session->checkcode, &eap_aka_session->checkcode_state);
		if (slen < 0) {
			RPEDEBUG("Failed calculating checkcode");
			goto failure;
		}
		eap_aka_session->checkcode_len = slen;

		MEM(vp = fr_pair_afrom_da(packet, attr_eap_aka_checkcode));
		fr_pair_value_memcpy(vp, eap_aka_session->checkcode, slen);
	/*
	 *	If we don't have checkcode data, then we exchanged
	 *	no identity packets, so checkcode is zero.
	 */
	} else {
		MEM(vp = fr_pair_afrom_da(packet, attr_eap_aka_checkcode));
		eap_aka_session->checkcode_len = 0;
	}
	fr_pair_replace(to_peer, vp);

	/*
	 *	We've sent the challenge so the peer should now be able
	 *	to accept encrypted attributes.
	 */
	eap_aka_session->allow_encrypted = true;

	/*
	 *	Encode the packet
	 */
	if (eap_aka_compose(eap_session) < 0) goto failure;

	return 0;
}
Beispiel #14
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 #15
0
/** Send the challenge itself
 *
 * Challenges will come from one of three places eventually:
 *
 * 1  from attributes like FR_EAP_SIM_RANDx
 *	    (these might be retrieved from a database)
 *
 * 2  from internally implemented SIM authenticators
 *	    (a simple one based upon XOR will be provided)
 *
 * 3  from some kind of SS7 interface.
 *
 * For now, they only come from attributes.
 * It might be that the best way to do 2/3 will be with a different
 * module to generate/calculate things.
 */
static int eap_sim_send_challenge(eap_session_t *eap_session)
{
	REQUEST			*request = eap_session->request;
	eap_sim_session_t	*eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
	VALUE_PAIR		**to_peer, *vp;
	RADIUS_PACKET		*packet;
	fr_sim_vector_src_t	src = SIM_VECTOR_SRC_AUTO;

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

	RDEBUG2("Acquiring GSM vector(s)");
	if ((fr_sim_vector_gsm_from_attrs(eap_session, request->control, 0, &eap_sim_session->keys, &src) != 0) ||
	    (fr_sim_vector_gsm_from_attrs(eap_session, request->control, 1, &eap_sim_session->keys, &src) != 0) ||
	    (fr_sim_vector_gsm_from_attrs(eap_session, request->control, 2, &eap_sim_session->keys, &src) != 0)) {
	    	REDEBUG("Failed retrieving SIM vectors");
		return RLM_MODULE_FAIL;
	}

	/*
	 *	All set, calculate keys!
	 */
	fr_sim_crypto_kdf_0_gsm(&eap_sim_session->keys);
	if (RDEBUG_ENABLED3) fr_sim_crypto_keys_log(request, &eap_sim_session->keys);

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

	/*
	 *	to_peer is the data to the client
	 */
	packet = eap_session->request->reply;
	to_peer = &packet->vps;

	/*
	 *	Okay, we got the challenges! Put them into attributes.
	 */
	MEM(vp = fr_pair_afrom_da(packet, attr_eap_sim_rand));
	fr_pair_value_memcpy(vp, eap_sim_session->keys.gsm.vector[0].rand, SIM_VECTOR_GSM_RAND_SIZE);
	fr_pair_add(to_peer, vp);

	MEM(vp = fr_pair_afrom_da(packet, attr_eap_sim_rand));
	fr_pair_value_memcpy(vp, eap_sim_session->keys.gsm.vector[1].rand, SIM_VECTOR_GSM_RAND_SIZE);
	fr_pair_add(to_peer, vp);

	MEM(vp = fr_pair_afrom_da(packet, attr_eap_sim_rand));
	fr_pair_value_memcpy(vp, eap_sim_session->keys.gsm.vector[2].rand, SIM_VECTOR_GSM_RAND_SIZE);
	fr_pair_add(to_peer, vp);

	/*
	 *	Set subtype to challenge.
	 */
	vp = fr_pair_afrom_da(packet, attr_eap_sim_subtype);
	vp->vp_uint16 = EAP_SIM_CHALLENGE;
	fr_pair_replace(to_peer, vp);

	/*
	 *	Indicate we'd like to use protected success messages
	 */
	if (eap_sim_session->send_result_ind) {
		MEM(vp = fr_pair_afrom_da(packet, attr_eap_sim_result_ind));
		vp->vp_bool = true;
		fr_pair_replace(to_peer, 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(to_peer, vp);

	/*
	 *	We've sent the challenge so the peer should now be able
	 *	to accept encrypted attributes.
	 */
	eap_sim_session->allow_encrypted = true;

	/*
	 *	Encode the packet
	 */
	if (eap_sim_compose(eap_session,
			    eap_sim_session->keys.gsm.nonce_mt, sizeof(eap_sim_session->keys.gsm.nonce_mt)) < 0) {
		fr_pair_list_free(&packet->vps);
		return -1;
	}

	return 0;
}
Beispiel #16
0
/** Parses the MS-SOH type/value (note: NOT type/length/value) data and update the sohvp list
 *
 * See section 2.2.4 of MS-SOH. Because there's no "length" field we CANNOT just skip
 * unknown types; we need to know their length ahead of time. Therefore, we abort
 * if we find an unknown type. Note that sohvp may still have been modified in the
 * failure case.
 *
 * @param request Current request
 * @param p binary blob
 * @param data_len length of blob
 * @return
 *	- 0 on success.
 *	- -1 on failure.
 */
static int eapsoh_mstlv(REQUEST *request, uint8_t const *p, unsigned int data_len)
{
	VALUE_PAIR *vp;
	uint8_t c;
	int t;
	char *q;

	while (data_len > 0) {
		c = *p++;
		data_len--;

		switch (c) {
		case 1:
			/* MS-Machine-Inventory-Packet
			 * MS-SOH section 2.2.4.1
			 */
			if (data_len < 18) {
				RDEBUG("insufficient data for MS-Machine-Inventory-Packet");
				return 0;
			}
			data_len -= 18;

			vp = pair_make_request("SoH-MS-Machine-OS-vendor", "Microsoft", T_OP_EQ);
			if (!vp) return 0;

			vp = pair_make_request("SoH-MS-Machine-OS-version", NULL, T_OP_EQ);
			if (!vp) return 0;

			vp->vp_integer = soh_pull_be_32(p); p+=4;

			vp = pair_make_request("SoH-MS-Machine-OS-release", NULL, T_OP_EQ);
			if (!vp) return 0;

			vp->vp_integer = soh_pull_be_32(p); p+=4;

			vp = pair_make_request("SoH-MS-Machine-OS-build", NULL, T_OP_EQ);
			if (!vp) return 0;

			vp->vp_integer = soh_pull_be_32(p); p+=4;

			vp = pair_make_request("SoH-MS-Machine-SP-version", NULL, T_OP_EQ);
			if (!vp) return 0;

			vp->vp_integer = soh_pull_be_16(p); p+=2;

			vp = pair_make_request("SoH-MS-Machine-SP-release", NULL, T_OP_EQ);
			if (!vp) return 0;

			vp->vp_integer = soh_pull_be_16(p); p+=2;

			vp = pair_make_request("SoH-MS-Machine-Processor", NULL, T_OP_EQ);
			if (!vp) return 0;

			vp->vp_integer = soh_pull_be_16(p); p+=2;
			break;

		case 2:
			/* MS-Quarantine-State - FIXME: currently unhandled
			 * MS-SOH 2.2.4.1
			 *
			 * 1 byte reserved
			 * 1 byte flags
			 * 8 bytes NT Time field (100-nanosec since 1 Jan 1601)
			 * 2 byte urilen
			 * N bytes uri
			 */
			p += 10;
			t = soh_pull_be_16(p);	/* t == uri len */
			p += 2;
			p += t;
			data_len -= 12 + t;
			break;

		case 3:
			/* MS-Packet-Info
			 * MS-SOH 2.2.4.3
			 */
			RDEBUG3("SoH MS-Packet-Info %s vers=%i", *p & 0x10 ? "request" : "response", *p & 0xf);
			p++;
			data_len--;
			break;

		case 4:
			/* MS-SystemGenerated-Ids - FIXME: currently unhandled
			 * MS-SOH 2.2.4.4
			 *
			 * 2 byte length
			 * N bytes (3 bytes IANA enterprise# + 1 byte component id#)
			 */
			t = soh_pull_be_16(p);
			p += 2;
			p += t;
			data_len -= 2 + t;
			break;

		case 5:
			/* MS-MachineName
			 * MS-SOH 2.2.4.5
			 *
			 * 1 byte namelen
			 * N bytes name
			 */
			t = soh_pull_be_16(p);
			p += 2;

			vp = pair_make_request("SoH-MS-Machine-Name", NULL, T_OP_EQ);
			if (!vp) return 0;

			vp->vp_strvalue = q = talloc_array(vp, char, t);
			vp->type = VT_DATA;

			memcpy(q, p, t);
			q[t] = 0;

			p += t;
			data_len -= 2 + t;
			break;

		case 6:
			/* MS-CorrelationId
			 * MS-SOH 2.2.4.6
			 *
			 * 24 bytes opaque binary which we might, in future, have
			 * to echo back to the client in a final SoHR
			 */
			vp = pair_make_request("SoH-MS-Correlation-Id", NULL, T_OP_EQ);
			if (!vp) return 0;

			fr_pair_value_memcpy(vp, p, 24);
			p += 24;
			data_len -= 24;
			break;

		case 7:
			/* MS-Installed-Shvs - FIXME: currently unhandled
			 * MS-SOH 2.2.4.7
			 *
			 * 2 bytes length
			 * N bytes (3 bytes IANA enterprise# + 1 byte component id#)
			 */
			t = soh_pull_be_16(p);
			p += 2;
			p += t;
			data_len -= 2 + t;
			break;

		case 8:
			/* MS-Machine-Inventory-Ex
			 * MS-SOH 2.2.4.8
			 *
			 * 4 bytes reserved
			 * 1 byte product type (client=1 domain_controller=2 server=3)
			 */
			p += 4;
			vp = pair_make_request("SoH-MS-Machine-Role", NULL, T_OP_EQ);
			if (!vp) return 0;

			vp->vp_integer = *p;
			p++;
			data_len -= 5;
			break;

		default:
			RDEBUG("SoH Unknown MS TV %i stopping", c);
			return 0;
		}
	}
	return 1;
}
Beispiel #17
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;
}
/*
 *	Preprocess a request.
 */
static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
{
	int r;
	rlm_preprocess_t *inst = instance;

	VALUE_PAIR *vp;

	/*
	 *	Mangle the username, to get rid of stupid implementation
	 *	bugs.
	 */
	rad_mangle(inst, request);

	if (inst->with_ascend_hack) {
		/*
		 *	If we're using Ascend systems, hack the NAS-Port-Id
		 *	in place, to go from Ascend's weird values to something
		 *	approaching rationality.
		 */
		ascend_nasport_hack(fr_pair_find_by_num(request->packet->vps, PW_NAS_PORT, 0, TAG_ANY),
				    inst->ascend_channels_per_line);
	}

	if (inst->with_cisco_vsa_hack) {
		/*
		 *	We need to run this hack because the h323-conf-id
		 *	attribute should be used.
		 */
		cisco_vsa_hack(request);
	}

	if (inst->with_alvarion_vsa_hack) {
		/*
		 *	We need to run this hack because the Alvarion
		 *	people are crazy.
		 */
		alvarion_vsa_hack(request->packet->vps);
	}

	if (inst->with_cablelabs_vsa_hack) {
		/*
		 *	We need to run this hack because the Cablelabs
		 *	people are crazy.
		 */
		cablelabs_vsa_hack(&request->packet->vps);
	}

	/*
	 *	Add an event timestamp. Means Event-Timestamp can be used
	 *	consistently instead of one letter expansions.
	 */
	vp = fr_pair_find_by_num(request->packet->vps, PW_EVENT_TIMESTAMP, 0, TAG_ANY);
	if (!vp) {
		vp = radius_pair_create(request->packet, &request->packet->vps, PW_EVENT_TIMESTAMP, 0);
		vp->vp_date = request->packet->timestamp.tv_sec;
	}

	/*
	 *	Note that we add the Request-Src-IP-Address to the request
	 *	structure BEFORE checking huntgroup access.  This allows
	 *	the Request-Src-IP-Address to be used for huntgroup
	 *	comparisons.
	 */
	if (add_nas_attr(request) < 0) {
		return RLM_MODULE_FAIL;
	}

	hints_setup(inst->hints, request);

	/*
	 *      If there is a PW_CHAP_PASSWORD attribute but there
	 *      is PW_CHAP_CHALLENGE we need to add it so that other
	 *	modules can use it as a normal attribute.
	 */
	if (fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) &&
	    fr_pair_find_by_num(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL) {
		vp = radius_pair_create(request->packet, &request->packet->vps, PW_CHAP_CHALLENGE, 0);
		fr_pair_value_memcpy(vp, request->packet->vector, AUTH_VECTOR_LEN);
	}

	if ((r = huntgroup_access(request, inst->huntgroups)) != RLM_MODULE_OK) {
		char buf[1024];
		RIDEBUG("No huntgroup access: [%s] (%s)",
			request->username ? request->username->vp_strvalue : "<NO User-Name>",
			auth_name(buf, sizeof(buf), request, 1));

		return r;
	}

	return RLM_MODULE_OK; /* Meaning: try next authorization module */
}
Beispiel #19
0
/** Send NONCE_S and re-key
 *
 */
static int eap_sim_send_reauthentication(eap_session_t *eap_session)
{
	REQUEST			*request = eap_session->request;
	eap_sim_session_t	*eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
	VALUE_PAIR		**to_peer, *vp, *mk, *counter;
	RADIUS_PACKET		*packet;

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

	/*
	 *	to_peer is the data to the client
	 */
	packet = eap_session->request->reply;
	to_peer = &packet->vps;

	/*
	 *	If any of the session resumption inputs (on our side)
	 *	are missing or malformed, return an error code
	 *	and the state machine will jump to the start state.
	 */
	mk = fr_pair_find_by_da(request->control, attr_eap_sim_mk, TAG_ANY);
	if (!mk) {
		RWDEBUG2("Missing &control:EAP-SIM-MK, skipping session resumption");
		return -1;
	}
	if (mk->vp_length != SIM_MK_SIZE) {
		RWDEBUG("&control:EAP-SIM-MK has incorrect length, expected %u bytes got %zu bytes",
			SIM_MK_SIZE, mk->vp_length);
		return -1;
	}
	counter = fr_pair_find_by_da(request->control, attr_eap_sim_counter, TAG_ANY);
	if (!counter) {
		RWDEBUG2("Missing &control:EAP-SIM-Counter, skipping session resumption");
		return -1;
	}

	/*
	 *	All set, calculate keys!
	 */
	fr_sim_crypto_keys_init_kdf_0_reauth(&eap_sim_session->keys, mk->vp_octets, counter->vp_uint16);
	fr_sim_crypto_kdf_0_reauth(&eap_sim_session->keys);
	if (RDEBUG_ENABLED3) fr_sim_crypto_keys_log(request, &eap_sim_session->keys);

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

	/*
	 *	Set subtype to challenge.
	 */
	vp = fr_pair_afrom_da(packet, attr_eap_sim_subtype);
	vp->vp_uint16 = EAP_SIM_REAUTH;
	fr_pair_replace(to_peer, vp);

	/*
	 *	Add nonce_s
	 */
	MEM(vp = fr_pair_afrom_da(packet, attr_eap_sim_nonce_s));
	fr_pair_value_memcpy(vp, eap_sim_session->keys.reauth.nonce_s, sizeof(eap_sim_session->keys.reauth.nonce_s));
	fr_pair_replace(to_peer, vp);

	/*
	 *	Indicate we'd like to use protected success messages
	 */
	if (eap_sim_session->send_result_ind) {
		MEM(vp = fr_pair_afrom_da(packet, attr_eap_sim_result_ind));
		vp->vp_bool = true;
		fr_pair_replace(to_peer, 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(to_peer, vp);

	/*
	 *	We've sent the challenge so the peer should now be able
	 *	to accept encrypted attributes.
	 */
	eap_sim_session->allow_encrypted = true;

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

	return 0;
}
Beispiel #20
0
/*
 *	Send one packet.
 */
static int send_one_packet(rc_request_t *request)
{
	assert(request->done == false);

	/*
	 *	Remember when we have to wake up, to re-send the
	 *	request, of we didn't receive a reply.
	 */
	if ((sleep_time == -1) || (sleep_time > (int) timeout)) sleep_time = (int) timeout;

	/*
	 *	Haven't sent the packet yet.  Initialize it.
	 */
	if (request->packet->id == -1) {
		int i;
		bool rcode;

		assert(request->reply == NULL);

		/*
		 *	Didn't find a free packet ID, we're not done,
		 *	we don't sleep, and we stop trying to process
		 *	this packet.
		 */
	retry:
		request->packet->src_ipaddr.af = server_ipaddr.af;
		rcode = fr_packet_list_id_alloc(pl, ipproto, &request->packet, NULL);
		if (!rcode) {
			int mysockfd;

#ifdef WITH_TCP
			if (proto) {
				mysockfd = fr_socket_client_tcp(NULL,
								&request->packet->dst_ipaddr,
								request->packet->dst_port, false);
			} else
#endif
			mysockfd = fr_socket(&client_ipaddr, 0);
			if (mysockfd < 0) {
				ERROR("Failed opening socket");
				exit(1);
			}
			if (!fr_packet_list_socket_add(pl, mysockfd, ipproto,
						       &request->packet->dst_ipaddr,
						       request->packet->dst_port, NULL)) {
				ERROR("Can't add new socket");
				exit(1);
			}
			goto retry;
		}

		assert(request->packet->id != -1);
		assert(request->packet->data == NULL);

		for (i = 0; i < 4; i++) {
			((uint32_t *) request->packet->vector)[i] = fr_rand();
		}

		/*
		 *	Update the password, so it can be encrypted with the
		 *	new authentication vector.
		 */
		if (request->password) {
			VALUE_PAIR *vp;

			if ((vp = fr_pair_find_by_num(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) {
				fr_pair_value_strcpy(vp, request->password->vp_strvalue);

			} else if ((vp = fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
				uint8_t buffer[17];

				rad_chap_encode(request->packet, buffer, fr_rand() & 0xff, request->password);
				fr_pair_value_memcpy(vp, buffer, 17);

			} else if (fr_pair_find_by_num(request->packet->vps, PW_MS_CHAP_PASSWORD, 0, TAG_ANY) != NULL) {
				mschapv1_encode(request->packet, &request->packet->vps, request->password->vp_strvalue);

			} else {
				DEBUG("WARNING: No password in the request");
			}
		}

		request->timestamp = time(NULL);
		request->tries = 1;
		request->resend++;

	} else {		/* request->packet->id >= 0 */
		time_t now = time(NULL);

		/*
		 *	FIXME: Accounting packets are never retried!
		 *	The Acct-Delay-Time attribute is updated to
		 *	reflect the delay, and the packet is re-sent
		 *	from scratch!
		 */

		/*
		 *	Not time for a retry, do so.
		 */
		if ((now - request->timestamp) < timeout) {
			/*
			 *	When we walk over the tree sending
			 *	packets, we update the minimum time
			 *	required to sleep.
			 */
			if ((sleep_time == -1) ||
			    (sleep_time > (now - request->timestamp))) {
				sleep_time = now - request->timestamp;
			}
			return 0;
		}

		/*
		 *	We're not trying later, maybe the packet is done.
		 */
		if (request->tries == retries) {
			assert(request->packet->id >= 0);

			/*
			 *	Delete the request from the tree of
			 *	outstanding requests.
			 */
			fr_packet_list_yank(pl, request->packet);

			REDEBUG("No reply from server for ID %d socket %d",
				request->packet->id, request->packet->sockfd);
			deallocate_id(request);

			/*
			 *	Normally we mark it "done" when we've received
			 *	the reply, but this is a special case.
			 */
			if (request->resend == resend_count) {
				request->done = true;
			}
			stats.lost++;
			return -1;
		}

		/*
		 *	We are trying later.
		 */
		request->timestamp = now;
		request->tries++;
	}

	/*
	 *	Send the packet.
	 */
	if (rad_send(request->packet, NULL, secret) < 0) {
		REDEBUG("Failed to send packet for ID %d", request->packet->id);
		deallocate_id(request);
		request->done = true;
		return -1;
	}

	fr_packet_header_print(fr_log_fp, request->packet, false);
	if (fr_debug_lvl > 0) vp_printlist(fr_log_fp, request->packet->vps);

	return 0;
}
Beispiel #21
0
/*
 *	Convert diameter attributes to our VALUE_PAIR's
 */
static VALUE_PAIR *diameter2vp(REQUEST *request, REQUEST *fake, SSL *ssl,
			       uint8_t const *data, size_t data_len)
{
	uint32_t	attr;
	uint32_t	vendor;
	uint32_t	length;
	size_t		offset;
	size_t		size;
	size_t		data_left = data_len;
	VALUE_PAIR	*first = NULL;
	VALUE_PAIR	*vp;
	RADIUS_PACKET	*packet = fake->packet; /* FIXME: api issues */
	vp_cursor_t	out;

	fr_cursor_init(&out, &first);

	while (data_left > 0) {
		rad_assert(data_left <= data_len);
		memcpy(&attr, data, sizeof(attr));
		data += 4;
		attr = ntohl(attr);
		vendor = 0;

		memcpy(&length, data, sizeof(length));
		data += 4;
		length = ntohl(length);

		/*
		 *	A "vendor" flag, with a vendor ID of zero,
		 *	is equivalent to no vendor.  This is stupid.
		 */
		offset = 8;
		if ((length & ((uint32_t)1 << 31)) != 0) {
			memcpy(&vendor, data, sizeof(vendor));
			vendor = ntohl(vendor);

			data += 4; /* skip the vendor field, it's zero */
			offset += 4; /* offset to value field */

			if (attr > 65535) goto next_attr;
			if (vendor > FR_MAX_VENDOR) goto next_attr;
		}

		/*
		 *	FIXME: Handle the M bit.  For now, we assume that
		 *	some other module takes care of any attribute
		 *	with the M bit set.
		 */

		/*
		 *	Get the length.
		 */
		length &= 0x00ffffff;

		/*
		 *	Get the size of the value portion of the
		 *	attribute.
		 */
		size = length - offset;

		/*
		 *	Vendor attributes can be larger than 255.
		 *	Normal attributes cannot be.
		 */
		if ((attr > 255) && (vendor == 0)) {
			RWDEBUG2("Skipping Diameter attribute %u", attr);
			goto next_attr;
		}

		/*
		 *	EAP-Message AVPs can be larger than 253 octets.
		 *
		 *	For now, we rely on the main decoder in
		 *	src/lib/radius to decode data into VPs.  This
		 *	means putting the data into a RADIUS attribute
		 *	format.  It also means that we can't handle
		 *	"extended" attributes in the Diameter space.  Oh well...
		 */
		if ((size > 253) && !((vendor == 0) && (attr == PW_EAP_MESSAGE))) {
			RWDEBUG2("diameter2vp skipping long attribute %u", attr);
			goto next_attr;
		}

		/*
		 *	RADIUS VSAs are handled as Diameter attributes
		 *	with Vendor-Id == 0, and the VSA data packed
		 *	into the "String" field as per normal.
		 *
		 *	EXCEPT for the MS-CHAP attributes.
		 */
		if ((vendor == 0) && (attr == PW_VENDOR_SPECIFIC)) {
			ssize_t decoded;
			uint8_t buffer[256];

			buffer[0] = PW_VENDOR_SPECIFIC;
			buffer[1] = size + 2;
			memcpy(buffer + 2, data, size);

			vp = NULL;
			decoded = rad_attr2vp(packet, NULL, NULL, NULL,
					      buffer, size + 2, &vp);
			if (decoded < 0) {
				REDEBUG2("diameter2vp failed decoding attr: %s",
					fr_strerror());
				goto do_octets;
			}

			if ((size_t) decoded != size + 2) {
				REDEBUG2("diameter2vp failed to entirely decode VSA");
				fr_pair_list_free(&vp);
				goto do_octets;
			}

			fr_cursor_merge(&out, vp);

			goto next_attr;
		}

		/*
		 *	Create it.  If this fails, it's because we're OOM.
		 */
	do_octets:
		vp = fr_pair_afrom_num(packet, attr, vendor);
		if (!vp) {
			RDEBUG2("Failure in creating VP");
			fr_pair_list_free(&first);
			return NULL;
		}

		/*
		 *	If it's a type from our dictionary, then
		 *	we need to put the data in a relevant place.
		 *
		 *	@todo: Export the lib/radius.c decoder, and use it here!
		 */
		switch (vp->da->type) {
		case PW_TYPE_INTEGER:
		case PW_TYPE_DATE:
			if (size != vp->vp_length) {
				DICT_ATTR const *da;

				/*
				 *	Bad format.  Create a "raw"
				 *	attribute.
				 */
		raw:
				if (vp) fr_pair_list_free(&vp);
				da = dict_unknown_afrom_fields(packet, attr, vendor);
				if (!da) return NULL;
				vp = fr_pair_afrom_da(packet, da);
				if (!vp) return NULL;
				fr_pair_value_memcpy(vp, data, size);
				break;
			}
			memcpy(&vp->vp_integer, data, vp->vp_length);

			/*
			 *	Stored in host byte order: change it.
			 */
			vp->vp_integer = ntohl(vp->vp_integer);
			break;

		case PW_TYPE_INTEGER64:
			if (size != vp->vp_length) goto raw;
			memcpy(&vp->vp_integer64, data, vp->vp_length);

			/*
			 *	Stored in host byte order: change it.
			 */
			vp->vp_integer64 = ntohll(vp->vp_integer64);
			break;

		case PW_TYPE_IPV4_ADDR:
			if (size != vp->vp_length) {
				RDEBUG2("Invalid length attribute %d",
				       attr);
				fr_pair_list_free(&first);
				fr_pair_list_free(&vp);
				return NULL;
			}
			memcpy(&vp->vp_ipaddr, data, vp->vp_length);

			/*
			 *	Stored in network byte order: don't change it.
			 */
			break;

		case PW_TYPE_BYTE:
			if (size != vp->vp_length) goto raw;
			vp->vp_byte = data[0];
			break;

		case PW_TYPE_SHORT:
			if (size != vp->vp_length) goto raw;
			vp->vp_short = (data[0] * 256) + data[1];
			break;

		case PW_TYPE_SIGNED:
			if (size != vp->vp_length) goto raw;
			memcpy(&vp->vp_signed, data, vp->vp_length);
			vp->vp_signed = ntohl(vp->vp_signed);
			break;

		case PW_TYPE_IPV6_ADDR:
			if (size != vp->vp_length) goto raw;
			memcpy(&vp->vp_ipv6addr, data, vp->vp_length);
			break;

		case PW_TYPE_IPV6_PREFIX:
			if (size != vp->vp_length) goto raw;
			memcpy(vp->vp_ipv6prefix, data, vp->vp_length);
			break;

		case PW_TYPE_STRING:
			fr_pair_value_bstrncpy(vp, data, size);
			vp->vp_length = strlen(vp->vp_strvalue); /* embedded zeros are NOT allowed */
			break;

			/*
			 *	Copy it over verbatim.
			 */
		case PW_TYPE_OCTETS:
		default:
			fr_pair_value_memcpy(vp, data, size);
			break;
		}

		/*
		 *	Ensure that the client is using the
		 *	correct challenge.  This weirdness is
		 *	to protect against against replay
		 *	attacks, where anyone observing the
		 *	CHAP exchange could pose as that user,
		 *	by simply choosing to use the same
		 *	challenge.
		 *
		 *	By using a challenge based on
		 *	information from the current session,
		 *	we can guarantee that the client is
		 *	not *choosing* a challenge.
		 *
		 *	We're a little forgiving in that we
		 *	have loose checks on the length, and
		 *	we do NOT check the Id (first octet of
		 *	the response to the challenge)
		 *
		 *	But if the client gets the challenge correct,
		 *	we're not too worried about the Id.
		 */
		if (((vp->da->vendor == 0) && (vp->da->attr == PW_CHAP_CHALLENGE)) ||
		    ((vp->da->vendor == VENDORPEC_MICROSOFT) && (vp->da->attr == PW_MSCHAP_CHALLENGE))) {
			uint8_t	challenge[16];

			if ((vp->vp_length < 8) ||
			    (vp->vp_length > 16)) {
				RDEBUG("Tunneled challenge has invalid length");
				fr_pair_list_free(&first);
				fr_pair_list_free(&vp);
				return NULL;
			}

			eapttls_gen_challenge(ssl, challenge,
					      sizeof(challenge));

			if (memcmp(challenge, vp->vp_octets,
				   vp->vp_length) != 0) {
				RDEBUG("Tunneled challenge is incorrect");
				fr_pair_list_free(&first);
				fr_pair_list_free(&vp);
				return NULL;
			}
		}

		/*
		 *	Update the list.
		 */
		fr_cursor_insert(&out, vp);

	next_attr:
		/*
		 *	Catch non-aligned attributes.
		 */
		if (data_left == length) break;

		/*
		 *	The length does NOT include the padding, so
		 *	we've got to account for it here by rounding up
		 *	to the nearest 4-byte boundary.
		 */
		length += 0x03;
		length &= ~0x03;

		rad_assert(data_left >= length);
		data_left -= length;
		data += length - offset; /* already updated */
	}

	/*
	 *	We got this far.  It looks OK.
	 */
	return first;
}
Beispiel #22
0
/*
 *	Generate a challenge to be presented to the user.
 */
static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
{
	rlm_otp_t *inst = (rlm_otp_t *) instance;

	char challenge[OTP_MAX_CHALLENGE_LEN + 1];	/* +1 for '\0' terminator */
	int auth_type_found;

	/* Early exit if Auth-Type != inst->name */
	{
		VALUE_PAIR *vp;

		auth_type_found = 0;
		vp = fr_pair_find_by_num(request->config, PW_AUTH_TYPE, 0, TAG_ANY);
		if (vp) {
			auth_type_found = 1;
			if (strcmp(vp->vp_strvalue, inst->name)) {
				return RLM_MODULE_NOOP;
			}
		}
	}

	/* The State attribute will be present if this is a response. */
	if (fr_pair_find_by_num(request->packet->vps, PW_STATE, 0, TAG_ANY) != NULL) {
		DEBUG("rlm_otp: autz: Found response to Access-Challenge");

		return RLM_MODULE_OK;
	}

	/* User-Name attribute required. */
	if (!request->username) {
		RWDEBUG("Attribute \"User-Name\" "
		       "required for authentication");

		return RLM_MODULE_INVALID;
	}

	if (otp_pwe_present(request) == 0) {
		RWDEBUG("Attribute "
			"\"User-Password\" or equivalent required "
			"for authentication");

		return RLM_MODULE_INVALID;
	}

	/*
	 * 	We used to check for special "challenge" and "resync" passcodes
	 * 	here, but these are complicated to explain and application is
	 * 	limited.  More importantly, since we've removed all actual OTP
	 * 	code (now we ask otpd), it's awkward for us to support them.
	 * 	Should the need arise to reinstate these options, the most
	 *	likely choice is to duplicate some otpd code here.
	 */
	if (inst->allow_sync && !inst->allow_async) {
		/* This is the token sync response. */
		if (!auth_type_found) {
			pair_make_config("Auth-Type", inst->name, T_OP_EQ);
		}

		return RLM_MODULE_OK;
	}

	/*
	 *	Generate a random challenge.
	 */
	otp_async_challenge(challenge, inst->challenge_len);

	/*
	 *	Create the State attribute, which will be returned to
	 *	us along with the response.
	 *
	 *	We will need this to verify the response.
	 *
	 *	It must be hmac protected to prevent insertion of arbitrary
	 *	State by an inside attacker.
	 *
	 *	If we won't actually use the State (server config doesn't
	 *	allow async), we just use a trivial State.
	 *
	 *	We always create at least a trivial State, so mod_authorize()
	 *	can quickly pass on to mod_authenticate().
	 */
	{
		int32_t now = htonl(time(NULL)); //!< Low-order 32 bits on LP64.

		char gen_state[OTP_MAX_RADSTATE_LEN];
		size_t len;
		VALUE_PAIR *vp;

		len = otp_gen_state(gen_state, challenge, inst->challenge_len,
				    0, now, inst->hmac_key);

		vp = fr_pair_afrom_num(request->reply, PW_STATE, 0);
		if (!vp) {
			return RLM_MODULE_FAIL;
		}

		fr_pair_value_memcpy(vp, (uint8_t const *) gen_state, len);
		fr_pair_add(&request->reply->vps, vp);
	}

	/*
	 *	Add the challenge to the reply.
	 */
	{
		VALUE_PAIR *vp;

		char *expanded = NULL;
		ssize_t len;

		/*
		 *	First add the internal OTP challenge attribute to
		 *	the reply list.
		 */
		vp = fr_pair_afrom_num(request->reply, PW_OTP_CHALLENGE, 0);
		if (!vp) {
			return RLM_MODULE_FAIL;
		}

		fr_pair_value_strcpy(vp, challenge);
		vp->op = T_OP_SET;

		fr_pair_add(&request->reply->vps, vp);

		/*
		 *	Then add the message to the user to they known
		 *	what the challenge value is.
		 */

		len = radius_axlat(&expanded, request, inst->chal_prompt, NULL, NULL);
		if (len < 0) {
			return RLM_MODULE_FAIL;
		}

		vp = fr_pair_afrom_num(request->reply, PW_REPLY_MESSAGE, 0);
		if (!vp) {
			talloc_free(expanded);
			return RLM_MODULE_FAIL;
		}

		(void) talloc_steal(vp, expanded);
		vp->vp_strvalue = expanded;
		vp->vp_length = len;
		vp->op = T_OP_SET;
		vp->type = VT_DATA;

		fr_pair_add(&request->reply->vps, vp);
	}

	/*
	 *	Mark the packet as an Access-Challenge packet.
	 * 	The server will take care of sending it to the user.
	 */
	request->reply->code = PW_CODE_ACCESS_CHALLENGE;
	DEBUG("rlm_otp: Sending Access-Challenge");

	if (!auth_type_found) {
		pair_make_config("Auth-Type", inst->name, T_OP_EQ);
	}

	return RLM_MODULE_HANDLED;
}
Beispiel #23
0
static FR_CODE eap_fast_eap_payload(REQUEST *request, eap_session_t *eap_session,
				    tls_session_t *tls_session, VALUE_PAIR *tlv_eap_payload)
{
	FR_CODE			code = FR_CODE_ACCESS_REJECT;
	rlm_rcode_t		rcode;
	VALUE_PAIR		*vp;
	eap_fast_tunnel_t	*t;
	REQUEST			*fake;

	RDEBUG2("Processing received EAP Payload");

	/*
	 * Allocate a fake REQUEST structure.
	 */
	fake = request_alloc_fake(request, NULL);
	rad_assert(!fake->packet->vps);

	t = talloc_get_type_abort(tls_session->opaque, eap_fast_tunnel_t);

	/*
	 * Add the tunneled attributes to the fake request.
	 */

	fake->packet->vps = fr_pair_afrom_da(fake->packet, attr_eap_message);
	fr_pair_value_memcpy(fake->packet->vps, tlv_eap_payload->vp_octets, tlv_eap_payload->vp_length, false);

	RDEBUG2("Got tunneled request");
	log_request_pair_list(L_DBG_LVL_1, request, fake->packet->vps, NULL);

	/*
	 * Tell the request that it's a fake one.
	 */
	MEM(fr_pair_add_by_da(fake->packet, &vp, &fake->packet->vps, attr_freeradius_proxied_to) >= 0);
	fr_pair_value_from_str(vp, "127.0.0.1", sizeof("127.0.0.1"), '\0', false);

	/*
	 * Update other items in the REQUEST data structure.
	 */
	fake->username = fr_pair_find_by_da(fake->packet->vps, attr_user_name, TAG_ANY);
	fake->password = fr_pair_find_by_da(fake->packet->vps, attr_user_password, TAG_ANY);

	/*
	 * No User-Name, try to create one from stored data.
	 */
	if (!fake->username) {
		/*
		 * No User-Name in the stored data, look for
		 * an EAP-Identity, and pull it out of there.
		 */
		if (!t->username) {
			vp = fr_pair_find_by_da(fake->packet->vps, attr_eap_message, TAG_ANY);
			if (vp &&
			    (vp->vp_length >= EAP_HEADER_LEN + 2) &&
			    (vp->vp_strvalue[0] == FR_EAP_CODE_RESPONSE) &&
			    (vp->vp_strvalue[EAP_HEADER_LEN] == FR_EAP_METHOD_IDENTITY) &&
			    (vp->vp_strvalue[EAP_HEADER_LEN + 1] != 0)) {
				/*
				 * Create & remember a User-Name
				 */
				MEM(t->username = fr_pair_afrom_da(t, attr_user_name));
				t->username->vp_tainted = true;
				fr_pair_value_bstrncpy(t->username, vp->vp_octets + 5, vp->vp_length - 5);

				RDEBUG2("Got tunneled identity of %pV", &t->username->data);
			} else {
				/*
				 * Don't reject the request outright,
				 * as it's permitted to do EAP without
				 * user-name.
				 */
				RWDEBUG2("No EAP-Identity found to start EAP conversation");
			}
		} /* else there WAS a t->username */

		if (t->username) {
			vp = fr_pair_copy(fake->packet, t->username);
			fr_pair_add(&fake->packet->vps, vp);
			fake->username = vp;
		}
	} /* else the request ALREADY had a User-Name */

	if (t->stage == EAP_FAST_AUTHENTICATION) {	/* FIXME do this only for MSCHAPv2 */
		VALUE_PAIR *tvp;

		tvp = fr_pair_afrom_da(fake, attr_eap_type);
		tvp->vp_uint32 = t->default_provisioning_method;
		fr_pair_add(&fake->control, tvp);

		/*
		 * RFC 5422 section 3.2.3 - Authenticating Using EAP-FAST-MSCHAPv2
		 */
		if (t->mode == EAP_FAST_PROVISIONING_ANON) {
			tvp = fr_pair_afrom_da(fake, attr_ms_chap_challenge);
			fr_pair_value_memcpy(tvp, t->keyblock->server_challenge, RADIUS_CHAP_CHALLENGE_LENGTH, false);
			fr_pair_add(&fake->control, tvp);
			RHEXDUMP(L_DBG_LVL_MAX, t->keyblock->server_challenge, RADIUS_CHAP_CHALLENGE_LENGTH, "MSCHAPv2 auth_challenge");

			tvp = fr_pair_afrom_da(fake, attr_ms_chap_peer_challenge);
			fr_pair_value_memcpy(tvp, t->keyblock->client_challenge, RADIUS_CHAP_CHALLENGE_LENGTH, false);
			fr_pair_add(&fake->control, tvp);
			RHEXDUMP(L_DBG_LVL_MAX, t->keyblock->client_challenge, RADIUS_CHAP_CHALLENGE_LENGTH, "MSCHAPv2 peer_challenge");
		}
	}

	/*
	 * Call authentication recursively, which will
	 * do PAP, CHAP, MS-CHAP, etc.
	 */
	eap_virtual_server(request, fake, eap_session, t->virtual_server);

	/*
	 * Decide what to do with the reply.
	 */
	switch (fake->reply->code) {
	case 0:			/* No reply code, must be proxied... */
#ifdef WITH_PROXY
		vp = fr_pair_find_by_da(fake->control, attr_proxy_to_realm, TAG_ANY);
		if (vp) {
			int			ret;
			eap_tunnel_data_t	*tunnel;

			RDEBUG2("Tunneled authentication will be proxied to %pV", &vp->data);

			/*
			 *	Tell the original request that it's going to be proxied.
			 */
			fr_pair_list_copy_by_da(request, &request->control, fake->control, attr_proxy_to_realm);

			/*
			 *	Seed the proxy packet with the tunneled request.
			 */
			rad_assert(!request->proxy);

			/*
			 *	FIXME: Actually proxy stuff
			 */
			request->proxy = request_alloc_fake(request, NULL);

			request->proxy->packet = talloc_steal(request->proxy, fake->packet);
			memset(&request->proxy->packet->src_ipaddr, 0,
			       sizeof(request->proxy->packet->src_ipaddr));
			memset(&request->proxy->packet->src_ipaddr, 0,
			       sizeof(request->proxy->packet->src_ipaddr));
			request->proxy->packet->src_port = 0;
			request->proxy->packet->dst_port = 0;
			fake->packet = NULL;
			fr_radius_packet_free(&fake->reply);
			fake->reply = NULL;

			/*
			 *	Set up the callbacks for the tunnel
			 */
			tunnel = talloc_zero(request, eap_tunnel_data_t);
			tunnel->tls_session = tls_session;

			/*
			 *	Associate the callback with the request.
			 */
			ret = request_data_add(request, request->proxy, REQUEST_DATA_EAP_TUNNEL_CALLBACK,
					       tunnel, false, false, false);
			fr_cond_assert(ret == 0);

			/*
			 *	rlm_eap.c has taken care of associating the eap_session
			 *	with the fake request.
			 *
			 *	So we associate the fake request with this request.
			 */
			ret = request_data_add(request, request->proxy, REQUEST_DATA_EAP_MSCHAP_TUNNEL_CALLBACK,
					       fake, true, false, false);
			fr_cond_assert(ret == 0);

			fake = NULL;

			/*
			 *	Didn't authenticate the packet, but we're proxying it.
			 */
			code = FR_CODE_STATUS_CLIENT;

		} else
#endif	/* WITH_PROXY */
		  {
			  REDEBUG("No tunneled reply was found, and the request was not proxied: rejecting the user");
			  code = FR_CODE_ACCESS_REJECT;
		  }
		break;

	default:
		/*
		 *	Returns RLM_MODULE_FOO, and we want to return FR_FOO
		 */
		rcode = process_reply(eap_session, tls_session, request, fake->reply);
		switch (rcode) {
		case RLM_MODULE_REJECT:
			code = FR_CODE_ACCESS_REJECT;
			break;

		case RLM_MODULE_HANDLED:
			code = FR_CODE_ACCESS_CHALLENGE;
			break;

		case RLM_MODULE_OK:
			code = FR_CODE_ACCESS_ACCEPT;
			break;

		default:
			code = FR_CODE_ACCESS_REJECT;
			break;
		}
		break;
	}

	talloc_free(fake);

	return code;
}
/*
 * given a radius request with an EAP-SIM body, decode it into TLV pairs
 *
 * return value is true if it succeeded, false if there was something
 * wrong and the packet should be discarded.
 *
 */
int unmap_eapsim_basictypes(RADIUS_PACKET *r,
			    uint8_t *attr, unsigned int attrlen)
{
	VALUE_PAIR	*newvp;
	int		eapsim_attribute;
	unsigned int	eapsim_len;
	int		es_attribute_count;

	es_attribute_count = 0;

	/* big enough to have even a single attribute */
	if (attrlen < 5) {
		ERROR("eap: EAP-Sim attribute too short: %d < 5", attrlen);
		return 0;
	}

	newvp = fr_pair_afrom_num(r, 0, PW_EAP_SIM_SUBTYPE);
	if (!newvp) {
		return 0;
	}

	newvp->vp_integer = attr[0];
	newvp->vp_length = 1;
	fr_pair_add(&(r->vps), newvp);

	attr     += 3;
	attrlen  -= 3;

	/* now, loop processing each attribute that we find */
	while (attrlen > 0) {
		if (attrlen < 2) {
			ERROR("eap: EAP-Sim attribute %d too short: %d < 2", es_attribute_count, attrlen);
			return 0;
		}

		eapsim_attribute = attr[0];
		eapsim_len = attr[1] * 4;

		if (eapsim_len > attrlen) {
			ERROR("eap: EAP-Sim attribute %d (no.%d) has length longer than data (%d > %d)",
			      eapsim_attribute, es_attribute_count, eapsim_len, attrlen);

			return 0;
		}

		if (eapsim_len > MAX_STRING_LEN) {
			eapsim_len = MAX_STRING_LEN;
		}
		if (eapsim_len < 2) {
			ERROR("eap: EAP-Sim attribute %d (no.%d) has length too small", eapsim_attribute,
			      es_attribute_count);
			       return 0;
		}

		newvp = fr_pair_afrom_num(r, 0, eapsim_attribute + PW_EAP_SIM_BASE);
		fr_pair_value_memcpy(newvp, &attr[2], eapsim_len - 2);
		fr_pair_add(&(r->vps), newvp);
		newvp = NULL;

		/* advance pointers, decrement length */
		attr += eapsim_len;
		attrlen -= eapsim_len;
		es_attribute_count++;
	}
	return 1;
}