示例#1
0
/** Compare module instances by parent and name
 *
 * The reason why we need parent, is because we could have submodules with names
 * that conflict with their parent.
 */
static int module_instance_name_cmp(void const *one, void const *two)
{
	module_instance_t const *a = one;
	module_instance_t const *b = two;
	dl_instance_t const	*dl_inst;
	int a_depth = 0, b_depth = 0;
	int ret;

	/*
	 *	Sort by depth, so for tree walking we start
	 *	at the shallowest node, and finish with
	 *	the deepest child.
	 */
	for (dl_inst = a->dl_inst; dl_inst; dl_inst = dl_inst->parent) a_depth++;
	for (dl_inst = b->dl_inst; dl_inst; dl_inst = dl_inst->parent) b_depth++;

	ret = (a_depth > b_depth) - (a_depth < b_depth);
	if (ret != 0) return ret;

	/*
	 *	This happens, as dl_inst is is used in
	 *	as the loop condition above.
	 */
#ifdef __clang_analyzer__
	if (!fr_cond_assert(a->dl_inst)) return +1;
	if (!fr_cond_assert(b->dl_inst)) return -1;
#endif

	ret = (a->dl_inst->parent > b->dl_inst->parent) - (a->dl_inst->parent < b->dl_inst->parent);
	if (ret != 0) return ret;

	return strcmp(a->name, b->name);
}
示例#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;
}
示例#3
0
static void xlat_delay_cancel(REQUEST *request, UNUSED void *instance, UNUSED void *thread,
			      void *rctx, fr_state_signal_t action)
{
	if (action != FR_SIGNAL_CANCEL) return;

	RDEBUG2("Cancelling delay");

	if (!fr_cond_assert(unlang_event_timeout_delete(request, rctx) == 0)) return;
}
示例#4
0
static ssize_t sim_decode_array(TALLOC_CTX *ctx, fr_cursor_t *cursor,
				fr_dict_attr_t const *parent,
				uint8_t const *data, size_t const attr_len, UNUSED size_t data_len,
				void *decoder_ctx)
{
	uint8_t const	*p = data, *end = p + attr_len;
	uint16_t	actual_len;
	int		elements, i;
	size_t		element_len;
	ssize_t		rcode;

	FR_PROTO_TRACE("Array attribute");

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

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

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

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

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

		p += rcode;

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

	return attr_len;	/* Say we consumed attr_len because it may have padding */
}
示例#5
0
/** Remove item from parent and fixup trees
 *
 * @param[in] parent	to remove child from.
 * @param[in] child	to remove.
 * @return
 *	- The item removed.
 *	- NULL if the item wasn't set.
 */
CONF_ITEM *cf_remove(CONF_ITEM *parent, CONF_ITEM *child)
{
	CONF_ITEM	*found;
	bool		in_ident1, in_ident2;

	if (!parent || !parent->child) return NULL;
	if (parent != child->parent) return NULL;

	for (found = fr_cursor_head(&parent->cursor);
	     found && (child != found);
	     found = fr_cursor_next(&parent->cursor));

	if (!found) return NULL;

	/*
	 *	Fixup the linked list
	 */
	found = fr_cursor_remove(&parent->cursor);
	if (!fr_cond_assert(found == child)) return NULL;

	in_ident1 = (rbtree_finddata(parent->ident1, child) == child);
	if (in_ident1 && (!rbtree_deletebydata(parent->ident1, child))) {
		rad_assert(0);
		return NULL;
	}

	in_ident2 = (rbtree_finddata(parent->ident2, child) == child);
	if (in_ident2 && (!rbtree_deletebydata(parent->ident2, child))) {
		rad_assert(0);
		return NULL;
	}

	/*
	 *	Look for twins
	 */
	for (found = fr_cursor_head(&parent->cursor);
	     found && (in_ident1 || in_ident2);
	     found = fr_cursor_next(&parent->cursor)) {
		if (in_ident1 && (_cf_ident1_cmp(found, child) == 0)) {
			rbtree_insert(parent->ident1, child);
			in_ident1 = false;
		}

		if (in_ident2 && (_cf_ident2_cmp(found, child) == 0)) {
			rbtree_insert(parent->ident2, child);
			in_ident2 = false;
		}
	}

	return child;
}
示例#6
0
/** Called when the timeout has expired
 *
 * Marks the request as resumable, and prints the delayed delay time.
 *
 * @param[in] request		The current request.
 * @param[in] instance		This instance of the delay module.
 * @param[in] thread		Thread specific module instance.
 * @param[in] ctx		Scheduled end of the delay.
 * @param[in] fired		When request processing was resumed.
 */
static void _delay_done(REQUEST *request, UNUSED void *instance, UNUSED void *thread, void *ctx, struct timeval *fired)
{
	struct timeval *yielded = talloc_get_type_abort(ctx, struct timeval);

	RDEBUG2("Delay done");

	/*
	 *	timeout should never be *before* the scheduled time,
	 *	if it is, something is very broken.
	 */
	if (!fr_cond_assert(fr_timeval_cmp(fired, yielded) >= 0)) REDEBUG("Unexpected resume time");

	unlang_resumable(request);
}
示例#7
0
/** Remove the current pair
 *
 * @todo this is really inefficient and should be fixed...
 *
 * The current VP will be set to the one before the VP being removed,
 * this is so the commonly used check and remove loop (below) works
 * as expected.
 @code {.c}
   for (vp = fr_pair_cursor_init(&cursor, head);
        vp;
        vp = fr_pair_cursor_next(&cursor) {
        if (<condition>) {
            vp = fr_pair_cursor_remove(&cursor);
            talloc_free(vp);
        }
   }
 @endcode
 *
 * @param cursor to remove the current pair from.
 * @return
 *	- #VALUE_PAIR we just replaced.
 *	- NULL on error.
 */
VALUE_PAIR *fr_pair_cursor_remove(vp_cursor_t *cursor)
{
	VALUE_PAIR *vp, *before;

	if (!fr_cond_assert(cursor->first)) return NULL;	/* cursor must have been initialised */

	vp = cursor->current;
	if (!vp) return NULL;

	/*
	 *	Where VP is head of the list
	 */
	if (*(cursor->first) == vp) {
		*(cursor->first) = vp->next;
		cursor->current = vp->next;
		cursor->next = vp->next ? vp->next->next : NULL;
		before = NULL;
		goto fixup;
	}

	/*
	 *	Where VP is not head of the list
	 */
	before = *(cursor->first);
	if (!before) return NULL;

	/*
	 *	Find the VP immediately preceding the one being removed
	 */
	while (before->next != vp) before = before->next;

	cursor->next = before->next = vp->next;	/* close the gap */
	cursor->current = before;		/* current jumps back one, but this is usually desirable */

fixup:
	vp->next = NULL;			/* limit scope of fr_pair_list_free() */

	/*
	 *	Fixup cursor->found if we removed the VP it was referring to,
	 *	and point to the previous one.
	 */
	if (vp == cursor->found) cursor->found = before;

	/*
	 *	Fixup cursor->last if we removed the VP it was referring to
	 */
	if (vp == cursor->last) cursor->last = cursor->current;
	return vp;
}
示例#8
0
/** Merges multiple VALUE_PAIR into the cursor
 *
 * Add multiple VALUE_PAIR from add to cursor.
 *
 * @param cursor to insert VALUE_PAIRs with
 * @param add one or more VALUE_PAIRs (may be NULL, which results in noop).
 */
void fr_pair_cursor_merge(vp_cursor_t *cursor, VALUE_PAIR *add)
{
	vp_cursor_t from;
	VALUE_PAIR *vp;

	if (!add) return;

	if (!fr_cond_assert(cursor->first)) return;	/* cursor must have been initialised */

	for (vp = fr_pair_cursor_init(&from, &add);
	     vp;
	     vp = fr_pair_cursor_next(&from)) {
	 	fr_pair_cursor_append(cursor, vp);
	}
}
示例#9
0
/** Get a notification that a vnode changed
 *
 * @param[in] el	the event list.
 * @param[in] sockfd	the socket which is ready to read.
 * @param[in] fflags	from kevent.
 * @param[in] ctx	the network socket context.
 */
static void fr_network_vnode_extend(UNUSED fr_event_list_t *el, int sockfd, int fflags, void *ctx)
{
	fr_network_socket_t *s = ctx;
	fr_network_t *nr = s->nr;

	fr_cond_assert(s->fd == sockfd);

	DEBUG3("network vnode");

	/*
	 *	Tell the IO handler that something has happened to the
	 *	file.
	 */
	s->listen->app_io->vnode(s->listen->app_io_instance, fflags);
}
示例#10
0
/** Returns the number of array members for arrays with fixed element sizes
 *
 * @param[out] out	The element length.
 * @param[in] len	the total length of the array.
 * @param[in] da	the specifying the array type.
 * @return
 *	- The number of elements in the array on success.
 *	- < 0 on error (array length not a multiple of element size).
 */
static int sim_array_members(size_t *out, size_t len, fr_dict_attr_t const *da)
{
	size_t		element_len;

	/*
	 *	Could be an array of bytes, integers, etc.
	 */
	switch (da->type) {
	case FR_TYPE_OCTETS:
		if (da->flags.length == 0) {
			fr_strerror_printf("%s: Octets array must have fixed length elements", __FUNCTION__);
			return -1;
		}
		element_len = da->flags.length;
		break;

	default:
		element_len = fr_sim_attr_sizes[da->type][0];
		break;
	}

	if (element_len == 1) {
		*out = 1;
		return 1;	/* Fast path */
	}

	if (!fr_cond_assert(element_len > 0)) return -1;

	if (element_len > len) {
		fr_strerror_printf("%s: Element length (%zu) > array length (%zu)", __FUNCTION__,
				   element_len, len);
		return -1;
	}

	/*
	 *	Number of elements must divide exactly
	 */
	if (len % element_len) {
		fr_strerror_printf("%s: Expected array actual length to be multiple of %zu, got %zu",
				   __FUNCTION__, element_len, len);
		return -1;
	}
	*out = element_len;

	return len / element_len;
}
示例#11
0
/** Insert a single VALUE_PAIR at the start of the list
 *
 * @note Will not advance cursor position to new attribute, but will set cursor
 *	 to this attribute, if it's the first one in the list.
 *
 * Insert a VALUE_PAIR at the start of the list.
 *
 * @param cursor to operate on.
 * @param vp to insert.
 */
void fr_pair_cursor_prepend(vp_cursor_t *cursor, VALUE_PAIR *vp)
{
	if (!fr_cond_assert(cursor->first)) return;	/* cursor must have been initialised */

	if (!vp) return;

	VP_VERIFY(vp);
	LIST_VERIFY(*(cursor->first));

	/*
	 *	Only allow one VP to by inserted at a time
	 */
	vp->next = NULL;

	/*
	 *	Cursor was initialised with a pointer to a NULL value_pair
	 */
	if (!*(cursor->first)) {
		*cursor->first = vp;
		cursor->current = vp;

		return;
	}

	/*
	 *	Append to the head of the list
	 */
	vp->next = *cursor->first;
	*cursor->first = vp;

	/*
	 *	Either current was never set, or something iterated to the
	 *	end of the attribute list. In both cases the newly inserted
	 *	VALUE_PAIR should be set as the current VALUE_PAIR.
	 */
	if (!cursor->current) cursor->current = vp;

	/*
	 *	If the next pointer was NULL, and the VALUE_PAIR
	 *	just added has a next pointer value, set the cursor's next
	 *	pointer to the VALUE_PAIR's next pointer.
	 */
	if (!cursor->next) cursor->next = cursor->current->next;

	LIST_VERIFY(*(cursor->first));
}
示例#12
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;
}
示例#13
0
/** Compare the first identifier of a child
 *
 * For CONF_ITEM_PAIR this is 'attr'.
 * For CONF_ITEM_SECTION this is 'name1'.
 * For CONF_ITEM_DATA this is 'type'.
 *
 * @param[in] a	First CONF_ITEM to compare.
 * @param[in] b Second CONF_ITEM to compare.
 * @return
 *	- >0 if a > b.
 *	- <0 if a < b.
 *	- 0 if a == b.
 */
static inline int _cf_ident1_cmp(void const *a, void const *b)
{
	CONF_ITEM_TYPE type;

	{
		CONF_ITEM const *one = a;
		CONF_ITEM const *two = b;

		if (one->type > two->type) return +1;
		if (one->type < two->type) return -1;

		type = one->type;
	}

	switch (type) {
	case CONF_ITEM_PAIR:
	{
		CONF_PAIR const *one = a;
		CONF_PAIR const *two = b;

		return strcmp(one->attr, two->attr);
	}

	case CONF_ITEM_SECTION:
	{
		CONF_SECTION const *one = a;
		CONF_SECTION const *two = b;

		return strcmp(one->name1, two->name1);
	}

	case CONF_ITEM_DATA:
	{
		CONF_DATA const *one = a;
		CONF_DATA const *two = b;

		return strcmp(one->type, two->type);
	}

	default:
		if (!fr_cond_assert(0)) return 0;
	}
}
示例#14
0
/** Install I/O handlers for the bind operation
 *
 * @param[in] c			connection to StartTLS on.
 * @param[in] bind_dn		Identity to bind with.
 * @param[in] password		Password to bind with.
 * @param[in] serverctrls	Extra controls to pass to the server.
 * @param[in] clientctrls	Extra controls to pass to libldap.
 * @return
 *	- 0 on success.
 *	- -1 on failure.
 */
int fr_ldap_bind_async(fr_ldap_connection_t *c,
		       char const *bind_dn, char const *password,
		       LDAPControl **serverctrls, LDAPControl **clientctrls)
{
	int			fd = -1;
	fr_ldap_bind_ctx_t	*bind_ctx;
	fr_event_list_t		*el;

	DEBUG2("Starting bind operation");

	MEM(bind_ctx = talloc_zero(c, fr_ldap_bind_ctx_t));
	bind_ctx->c = c;

	/*
	 *	Bind as anonymous user
	 */
	bind_ctx->bind_dn = bind_dn ? bind_dn : "";
	bind_ctx->password = password;
	bind_ctx->serverctrls = serverctrls;
	bind_ctx->clientctrls = clientctrls;

	el = fr_connection_get_el(c->conn);

	if (ldap_get_option(c->handle, LDAP_OPT_DESC, &fd) == LDAP_SUCCESS) {
		int ret;

		ret = fr_event_fd_insert(bind_ctx, el, fd,
					 NULL,
					 _ldap_bind_io_write,
					 _ldap_bind_io_error,
					 bind_ctx);
		if (!fr_cond_assert(ret == 0)) {
			talloc_free(bind_ctx);
			return -1;
		}
	} else {
		_ldap_bind_io_write(el, -1, 0, bind_ctx);
	}

	return 0;
}
示例#15
0
/** Yield a request back to the interpreter from within a module
 *
 * This passes control of the request back to the unlang interpreter, setting
 * callbacks to execute when the request is 'signalled' asynchronously, or whatever
 * timer or I/O event the module was waiting for occurs.
 *
 * @note The module function which calls #unlang_module_yield should return control
 *	of the C stack to the unlang interpreter immediately after calling #unlang_module_yield.
 *	A common pattern is to use ``return unlang_module_yield(...)``.
 *
 * @param[in] request		The current request.
 * @param[in] resume		Called on unlang_resumable().
 * @param[in] signal		Called on unlang_action().
 * @param[in] rctx		to pass to the callbacks.
 * @return
 *	- RLM_MODULE_YIELD on success.
 *	- RLM_MODULE_FAIL (or asserts) if the current frame is not a module call or
 *	  resume frame.
 */
rlm_rcode_t unlang_module_yield(REQUEST *request,
				fr_unlang_module_resume_t resume, fr_unlang_module_signal_t signal, void *rctx)
{
	unlang_stack_t			*stack = request->stack;
	unlang_stack_frame_t		*frame = &stack->frame[stack->depth];
	unlang_resume_t			*mr;

	rad_assert(stack->depth > 0);

	REQUEST_VERIFY(request);	/* Check the yielded request is sane */

	switch (frame->instruction->type) {
	case UNLANG_TYPE_MODULE:
		mr = unlang_resume_alloc(request, (void *)resume, (void *)signal, rctx);
		if (!fr_cond_assert(mr)) {
			return RLM_MODULE_FAIL;
		}
		return RLM_MODULE_YIELD;

	case UNLANG_TYPE_RESUME:
		mr = talloc_get_type_abort(frame->instruction, unlang_resume_t);
		rad_assert(mr->parent->type == UNLANG_TYPE_MODULE);

		/*
		 *	Re-use the current RESUME frame, but over-ride
		 *	the callbacks and context.
		 */
		mr->resume = (void *)resume;
		mr->signal = (void *)signal;
		mr->rctx = rctx;

		return RLM_MODULE_YIELD;

	default:
		rad_assert(0);
		return RLM_MODULE_FAIL;
	}
}
示例#16
0
/** Compare only the second identifier of a child
 *
 * For CONF_ITEM_SECTION this is 'name2'.
 * For CONF_ITEM_DATA this is 'name'.
 *
 * @param[in] a	First CONF_ITEM to compare.
 * @param[in] b Second CONF_ITEM to compare.
 * @return
 *	- >0 if a > b.
 *	- <0 if a < b.
 *	- 0 if a == b.
 */
static inline int cf_ident2_cmp(void const *a, void const *b)
{
	CONF_ITEM const *ci = a;

	switch (ci->type) {
	case CONF_ITEM_PAIR:
		return 0;

	case CONF_ITEM_SECTION:
	{
		CONF_SECTION const *one = a;
		CONF_SECTION const *two = b;

		if (!two->name2 && one->name2) return +1;
		if (two->name2 && !one->name2) return -1;
		if (!two->name2 && !one->name2) return 0;

		return strcmp(one->name2, two->name2);
	}

	case CONF_ITEM_DATA:
	{
		CONF_DATA const *one = a;
		CONF_DATA const *two = b;

		if (!two->name && one->name) return +1;
		if (two->name && !one->name) return -1;
		if (!two->name && !one->name) return 0;

		return strcmp(one->name, two->name);
	}

	default:
		if (!fr_cond_assert(0)) return 0;
	}
}
示例#17
0
/** Handle authorization requests using Couchbase document data
 *
 * Attempt to fetch the document assocaited with the requested user by
 * using the deterministic key defined in the configuration.  When a valid
 * document is found it will be parsed and the containing value pairs will be
 * injected into the request.
 *
 * @param instance	The module instance.
 * @param thread	specific data.
 * @param request	The authorization request.
 * @return Operation status (#rlm_rcode_t).
 */
static rlm_rcode_t mod_authorize(void *instance, UNUSED void *thread, REQUEST *request)
{
	rlm_couchbase_t const	*inst = instance;		/* our module instance */
	rlm_couchbase_handle_t	*handle = NULL;			/* connection pool handle */
	char			buffer[MAX_KEY_SIZE];
	char const		*dockey;			/* our document key */
	lcb_error_t		cb_error = LCB_SUCCESS;		/* couchbase error holder */
	rlm_rcode_t		rcode = RLM_MODULE_OK;		/* return code */
	ssize_t			slen;

	/* assert packet as not null */
	rad_assert(request->packet != NULL);

	/* attempt to build document key */
	slen = tmpl_expand(&dockey, buffer, sizeof(buffer), request, inst->user_key, NULL, NULL);
	if (slen < 0) return RLM_MODULE_FAIL;
	if ((dockey == buffer) && is_truncated((size_t)slen, sizeof(buffer))) {
		REDEBUG("Key too long, expected < " STRINGIFY(sizeof(buffer)) " bytes, got %zi bytes", slen);
		return RLM_MODULE_FAIL;
	}

	/* get handle */
	handle = fr_pool_connection_get(inst->pool, request);

	/* check handle */
	if (!handle) return RLM_MODULE_FAIL;

	/* set couchbase instance */
	lcb_t cb_inst = handle->handle;

	/* set cookie */
	cookie_t *cookie = handle->cookie;

	/* fetch document */
	cb_error = couchbase_get_key(cb_inst, cookie, dockey);

	/* check error */
	if (cb_error != LCB_SUCCESS || !cookie->jobj) {
		/* log error */
		RERROR("failed to fetch document or parse return");
		/* set return */
		rcode = RLM_MODULE_FAIL;
		/* return */
		goto finish;
	}

	/* debugging */
	RDEBUG3("parsed user document == %s", json_object_to_json_string(cookie->jobj));

	{
		TALLOC_CTX	*pool = talloc_pool(request, 1024);	/* We need to do lots of allocs */
		fr_cursor_t	maps, vlms;
		vp_map_t	*map_head = NULL, *map;
		vp_list_mod_t	*vlm_head = NULL, *vlm;

		fr_cursor_init(&maps, &map_head);

		/*
		 *	Convert JSON data into maps
		 */
		if ((mod_json_object_to_map(pool, &maps, request, cookie->jobj, PAIR_LIST_CONTROL) < 0) ||
		    (mod_json_object_to_map(pool, &maps, request, cookie->jobj, PAIR_LIST_REPLY) < 0) ||
		    (mod_json_object_to_map(pool, &maps, request, cookie->jobj, PAIR_LIST_REQUEST) < 0) ||
		    (mod_json_object_to_map(pool, &maps, request, cookie->jobj, PAIR_LIST_STATE) < 0)) {
		invalid:
			talloc_free(pool);
			rcode = RLM_MODULE_INVALID;
			goto finish;
		}

		fr_cursor_init(&vlms, &vlm_head);

		/*
		 *	Convert all the maps into list modifications,
		 *	which are guaranteed to succeed.
		 */
		for (map = fr_cursor_head(&maps);
		     map;
		     map = fr_cursor_next(&maps)) {
			if (map_to_list_mod(pool, &vlm, request, map, NULL, NULL) < 0) goto invalid;
			fr_cursor_insert(&vlms, vlm);
		}

		if (!vlm_head) {
			RDEBUG2("Nothing to update");
			talloc_free(pool);
			rcode = RLM_MODULE_NOOP;
			goto finish;
		}

		/*
		 *	Apply the list of modifications
		 */
		for (vlm = fr_cursor_head(&vlms);
		     vlm;
		     vlm = fr_cursor_next(&vlms)) {
			int ret;

			ret = map_list_mod_apply(request, vlm);	/* SHOULD NOT FAIL */
			if (!fr_cond_assert(ret == 0)) {
				talloc_free(pool);
				rcode = RLM_MODULE_FAIL;
				goto finish;
			}
		}

		talloc_free(pool);
	}

finish:
	/* free json object */
	if (cookie->jobj) {
		json_object_put(cookie->jobj);
		cookie->jobj = NULL;
	}

	/* release handle */
	if (handle) fr_pool_connection_release(inst->pool, request, handle);

	/* return */
	return rcode;
}
示例#18
0
/** Compile virtual server sections
 *
 * Called twice, once when a server with an eap-aka namespace is found, and once
 * when an EAP-AKA module is instantiated.
 *
 * The first time is with actions == NULL and is to compile the sections and
 * perform validation.
 * The second time is to write out pointers to the compiled sections which the
 * EAP-AKA module will use to execute unlang code.
 *
 */
static int mod_section_compile(eap_aka_actions_t *actions, CONF_SECTION *server_cs)
{
	bool found = false;

	if (!fr_cond_assert(server_cs)) return -1;

	/*
	 *	Initial Identity-Response
	 *
	 *	We then either:
	 *	- Request a new identity
	 *	- Start full authentication
	 *	- Start fast re-authentication
	 *	- Fail...
	 */
	ACTION_SECTION(actions, recv_eap_identity_response, "recv", "EAP-Identity-Response");

	/*
	 *	Identity negotiation
	 */
	ACTION_SECTION(actions, send_identity_request, "send", "Identity-Request");
	ACTION_SECTION(actions, recv_identity_response, "recv", "Identity-Response");

	/*
	 *	Full-Authentication
	 */
	ACTION_SECTION(actions, send_challenge_request, "send", "Challenge-Request");
	ACTION_SECTION(actions, recv_challenge_response, "recv", "Challenge-Response");

	/*
	 *	Fast-Re-Authentication
	 */
	ACTION_SECTION(actions, send_fast_reauth_request, "send", "Fast-Reauth-Request");
	ACTION_SECTION(actions, recv_fast_reauth_response, "recv", "Fast-Reauth-Response");

	/*
	 *	Failures originating from the supplicant
	 */
	ACTION_SECTION(actions, recv_client_error, "recv", "Client-Error");
	ACTION_SECTION(actions, recv_authentication_reject, "recv", "Authentication-Reject");
	ACTION_SECTION(actions, recv_syncronization_failure, "recv", "Syncronization-Failure");

	/*
	 *	Failure originating from the server
	 */
	ACTION_SECTION(actions, send_failure_notification, "send", "Failure-Notification");
	ACTION_SECTION(actions, recv_failure_notification_ack, "recv", "Failure-Notification-ACK");

	/*
	 *	Protected success indication
	 */
	ACTION_SECTION(actions, send_success_notification, "send", "Success-Notification");
	ACTION_SECTION(actions, recv_success_notification_ack, "recv", "Success-Notification-ACK");

	/*
	 *	Final EAP-Success and EAP-Failure messages
	 */
	ACTION_SECTION(actions, send_eap_success, "send", "EAP-Success");
	ACTION_SECTION(actions, send_eap_failure, "send", "EAP-Failure");

	/*
	 *	Fast-Reauth vectors
	 */
	ACTION_SECTION(actions, load_session, "load", "session");
	ACTION_SECTION(actions, store_session, "store", "session");
	ACTION_SECTION(actions, clear_session, "clear", "session");

	/*
	 *	Warn if we couldn't find any actions.
	 */
	if (!found) {
		cf_log_warn(server_cs, "No \"eap-aka\" actions found in virtual server \"%s\"",
			    cf_section_name2(server_cs));
	}

	return 0;
}
示例#19
0
/** Return the next child that's of the specified type with the specified identifiers
 *
 * @param[in] parent	The section we're searching in.
 * @param[in] prev	item we found, or NULL to start from the beginning.
 * @param[in] type	of #CONF_ITEM we're searching for.
 * @param[in] ident1	The first identifier.
 * @param[in] ident2	The second identifier. Special value CF_IDENT_ANY
 *			can be used to match any ident2 value.
 * @return
 *	- The first matching item.
 *	- NULL if no items matched.
 */
static CONF_ITEM *cf_find_next(CONF_ITEM const *parent, CONF_ITEM const *prev,
			       CONF_ITEM_TYPE type, char const *ident1, char const *ident2)
{
	CONF_SECTION	cs_find;
	CONF_PAIR	cp_find;
	CONF_DATA	cd_find;
	CONF_ITEM	*find;
	CONF_ITEM	*ci;

	if (!parent) return NULL;

	if (!prev) {
		if (!ident1) return cf_next(parent, NULL, type);
		return cf_find(parent, type, ident1, ident2);
	}
	if (!ident1) return cf_next(parent, prev, type);

	switch (type) {
	case CONF_ITEM_SECTION:
		memset(&cs_find, 0, sizeof(cs_find));
		cs_find.item.type = CONF_ITEM_SECTION;
		cs_find.name1 = ident1;
		if (!IS_WILDCARD(ident2)) cs_find.name2 = ident2;

		find = (CONF_ITEM *)&cs_find;
		break;

	case CONF_ITEM_PAIR:
		rad_assert((ident2 == NULL) || IS_WILDCARD(ident2));

		memset(&cp_find, 0, sizeof(cp_find));
		cp_find.item.type = CONF_ITEM_PAIR;
		cp_find.attr = ident1;

		find = (CONF_ITEM *)&cp_find;
		break;

	case CONF_ITEM_DATA:
		memset(&cd_find, 0, sizeof(cd_find));
		cd_find.item.type = CONF_ITEM_DATA;
		cd_find.type = ident1;
		if (!IS_WILDCARD(ident2)) cd_find.name = ident2;

		find = (CONF_ITEM *)&cd_find;
		break;

	default:
		if (!fr_cond_assert(0)) return NULL;
	}

	if (IS_WILDCARD(ident1)) {
		for (ci = prev->next;
		     ci && (cf_ident2_cmp(ci, find) != 0);
		     ci = ci->next);

		return ci;
	}

	if (IS_WILDCARD(ident2)) {
		for (ci = prev->next;
		     ci && (_cf_ident1_cmp(ci, find) != 0);
		     ci = ci->next) {
			rad_assert(ci->next != ci);
		}

		return ci;
	}

	for (ci = prev->next;
	     ci && (_cf_ident2_cmp(ci, find) != 0);
	     ci = ci->next);

	return ci;
}
示例#20
0
/** Insert a single VALUE_PAIR at the end of the list
 *
 * @note Will not advance cursor position to new attribute, but will set cursor
 *	 to this attribute, if it's the first one in the list.
 *
 * Insert a VALUE_PAIR at the end of the list.
 *
 * @param cursor to operate on.
 * @param vp to insert.
 */
void fr_pair_cursor_append(vp_cursor_t *cursor, VALUE_PAIR *vp)
{
	VALUE_PAIR *i;

	if (!fr_cond_assert(cursor->first)) return;	/* cursor must have been initialised */

	if (!vp) return;

	VP_VERIFY(vp);
	LIST_VERIFY(*(cursor->first));

	/*
	 *	Only allow one VP to by inserted at a time
	 */
	vp->next = NULL;

	/*
	 *	Cursor was initialised with a pointer to a NULL value_pair
	 */
	if (!*(cursor->first)) {
		*cursor->first = vp;
		cursor->current = vp;

		return;
	}

	/*
	 *	We don't yet know where the last VALUE_PAIR is
	 *
	 *	Assume current is closer to the end of the list and
	 *	use that if available.
	 */
	if (!cursor->last) cursor->last = cursor->current ? cursor->current : *cursor->first;

	VP_VERIFY(cursor->last);

	/*
	 *	Wind last to the end of the list.
	 */
	if (cursor->last->next) {
		for (i = cursor->last; i; i = i->next) {
			VP_VERIFY(i);
			cursor->last = i;
		}
	}

	/*
	 *	Either current was never set, or something iterated to the
	 *	end of the attribute list. In both cases the newly inserted
	 *	VALUE_PAIR should be set as the current VALUE_PAIR.
	 */
	if (!cursor->current) cursor->current = vp;

	/*
	 *	Add the VALUE_PAIR to the end of the list
	 */
	cursor->last->next = vp;
	cursor->last = vp;	/* Wind it forward a little more */

	/*
	 *	If the next pointer was NULL, and the VALUE_PAIR
	 *	just added has a next pointer value, set the cursor's next
	 *	pointer to the VALUE_PAIR's next pointer.
	 */
	if (!cursor->next) cursor->next = cursor->current->next;

	LIST_VERIFY(*(cursor->first));
}
示例#21
0
/** Return the next child that's of the specified type with the specified identifiers
 *
 * @param[in] parent	The section we're searching in.
 * @param[in] type	of #CONF_ITEM we're searching for.
 * @param[in] ident1	The first identifier.
 * @param[in] ident2	The second identifier. Special value CF_IDENT_ANY
 *			can be used to match any ident2 value.
 * @return
 *	- The first matching item.
 *	- NULL if no items matched.
 */
static CONF_ITEM *cf_find(CONF_ITEM const *parent, CONF_ITEM_TYPE type, char const *ident1, char const *ident2)
{
	CONF_SECTION	cs_find;
	CONF_PAIR	cp_find;
	CONF_DATA	cd_find;
	CONF_ITEM	*find;

	if (!parent) return NULL;
	if (!parent->child) return NULL;	/* No children */

	if (!ident1) return cf_next(parent, NULL, type);

	switch (type) {
	case CONF_ITEM_SECTION:
		memset(&cs_find, 0, sizeof(cs_find));
		cs_find.item.type = CONF_ITEM_SECTION;
		cs_find.name1 = ident1;
		if (!IS_WILDCARD(ident2)) cs_find.name2 = ident2;

		find = (CONF_ITEM *)&cs_find;
		break;

	case CONF_ITEM_PAIR:
		rad_assert((ident2 == NULL) || IS_WILDCARD(ident2));

		memset(&cp_find, 0, sizeof(cp_find));
		cp_find.item.type = CONF_ITEM_PAIR;
		cp_find.attr = ident1;

		find = (CONF_ITEM *)&cp_find;
		break;

	case CONF_ITEM_DATA:
		memset(&cd_find, 0, sizeof(cd_find));
		cd_find.item.type = CONF_ITEM_DATA;
		cd_find.type = ident1;
		if (!IS_WILDCARD(ident2)) cd_find.name = ident2;

		find = (CONF_ITEM *)&cd_find;
		break;

	default:
		if (!fr_cond_assert(0)) return NULL;
	}

	/*
	 *	No ident1, iterate over the child list
	 */
	if (IS_WILDCARD(ident1)) {
		CONF_ITEM *ci;

		for (ci = parent->child;
		     ci && (cf_ident2_cmp(find, ci) != 0);
		     ci = ci->next);

		return ci;
	}

	/*
	 *	No ident2, use the ident1 tree.
	 */
	if (IS_WILDCARD(ident2)) return rbtree_finddata(parent->ident1, find);

	/*
	 *	Both ident1 and ident2 use the ident2 tree.
	 */
	return rbtree_finddata(parent->ident2, find);
}
示例#22
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;
}
示例#23
0
/** Send a bind request to a aserver
 *
 * @param[in] el	the event occurred in.
 * @param[in] fd	the event occurred on.
 * @param[in] flags	from kevent.
 * @param[in] uctx	bind_ctx containing credentials, and connection config/handle.
 */
static void _ldap_bind_io_write(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
{
	fr_ldap_bind_ctx_t	*bind_ctx = talloc_get_type_abort(uctx, fr_ldap_bind_ctx_t);
	fr_ldap_connection_t	*c = bind_ctx->c;

	LDAPControl		*our_serverctrls[LDAP_MAX_CONTROLS];
	LDAPControl		*our_clientctrls[LDAP_MAX_CONTROLS];

	struct timeval		tv = { 0, 0 };

	int			ret;
	struct berval		cred;

	fr_ldap_control_merge(our_serverctrls, our_clientctrls,
			      sizeof(our_serverctrls) / sizeof(*our_serverctrls),
			      sizeof(our_clientctrls) / sizeof(*our_clientctrls),
			      c, bind_ctx->serverctrls, bind_ctx->clientctrls);

	/*
	 *	Set timeout to be 0.0, which is the magic
	 *	non-blocking value.
	 */
	(void) ldap_set_option(c->handle, LDAP_OPT_NETWORK_TIMEOUT, &tv);

	if (bind_ctx->password) {
		memcpy(&cred.bv_val, &bind_ctx->password, sizeof(cred.bv_val));
		cred.bv_len = talloc_array_length(bind_ctx->password) - 1;
	} else {
		cred.bv_val = NULL;
		cred.bv_len = 0;
	}

	/*
	 *	Yes, confusingly named.  This is the simple version
	 *	of the SASL bind function that should always be
	 *	available.
	 */
	ret = ldap_sasl_bind(c->handle, bind_ctx->bind_dn, LDAP_SASL_SIMPLE, &cred,
			     our_serverctrls, our_clientctrls, &bind_ctx->msgid);
	switch (ret) {
	/*
	 *	If the handle was not connected, this operation
	 *	can return either LDAP_X_CONNECTING or LDAP_SUCCESS
	 *	depending on how fast the connection came up
	 *	and whether it was connectionless.
	 */
	case LDAP_X_CONNECTING:					/* Connection in progress - retry later */
		ret = ldap_get_option(c->handle, LDAP_OPT_DESC, &fd);
		if (!fr_cond_assert(ret == LDAP_OPT_SUCCESS)) {
		error:
			talloc_free(bind_ctx);
			fr_ldap_connection_timeout_reset(c);
			fr_ldap_state_error(c);			/* Restart the connection state machine */
			return;
		}

		ret = fr_event_fd_insert(bind_ctx, el, fd,
					 NULL,
					 _ldap_bind_io_write,	/* We'll be called again when the conn is open */
					 _ldap_bind_io_error,
					 bind_ctx);
		if (!fr_cond_assert(ret == 0)) goto error;
		break;

	case LDAP_SUCCESS:
		ret = fr_event_fd_insert(bind_ctx, el, fd,
					 _ldap_bind_io_read,
					 NULL,
					 _ldap_bind_io_error,
					 bind_ctx);
		if (!fr_cond_assert(ret == 0)) goto error;
		break;

	default:
		ERROR("Bind failed: %s", ldap_err2string(ret));
		goto error;
	}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

			p += sim_at_len;
			continue;
		}

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

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

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

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

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

	return attr_len;
}
示例#25
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 */
}
示例#26
0
static xlat_action_t xlat_delay(TALLOC_CTX *ctx, UNUSED fr_cursor_t *out,
				REQUEST *request, void const *xlat_inst, UNUSED void *xlat_thread_inst,
				fr_value_box_t **in)
{
	rlm_delay_t const	*inst;
	void			*instance;
	struct timeval		resume_at, delay, *yielded_at;

	memcpy(&instance, xlat_inst, sizeof(instance));	/* Stupid const issues */

	inst = talloc_get_type_abort(instance, rlm_delay_t);

	/*
	 *	Record the time that we yielded the request
	 */
	MEM(yielded_at = talloc(request, struct timeval));
	if (gettimeofday(yielded_at, NULL) < 0) {
		REDEBUG("Failed getting current time: %s", fr_syserror(errno));
		return XLAT_ACTION_FAIL;
	}

	/*
	 *	If there's no input delay, just yield and
	 *	immediately re-enqueue the request.
	 *	This is very useful for testing.
	 */
	if (!*in) {
		memset(&delay, 0, sizeof(delay));
		if (!fr_cond_assert(delay_add(request, &resume_at, yielded_at, &delay, true, true) == 0)) {
			return XLAT_ACTION_FAIL;
		}
		goto yield;
	}

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

	if (fr_timeval_from_str(&delay, (*in)->vb_strvalue) < 0) {
		RPEDEBUG("Failed parsing delay time");
		talloc_free(yielded_at);
		return XLAT_ACTION_FAIL;
	}

	if (delay_add(request, &resume_at, yielded_at, &delay, inst->force_reschedule, inst->relative) != 0) {
		RDEBUG2("Not adding delay");
		talloc_free(yielded_at);
		return XLAT_ACTION_DONE;
	}

yield:
	RDEBUG3("Current time %pV, resume time %pV", fr_box_timeval(*yielded_at), fr_box_timeval(resume_at));

	if (unlang_xlat_event_timeout_add(request, _delay_done, yielded_at, &resume_at) < 0) {
		RPEDEBUG("Adding event failed");
		return XLAT_ACTION_FAIL;
	}

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

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

	FR_PROTO_HEX_DUMP(data, attr_len, "ciphertext");

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

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

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

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

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

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

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

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

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

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

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

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

	EVP_CIPHER_CTX_free(evp_ctx);

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

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

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

	*out = decr;

	return decr_len;
}
示例#28
0
/** Read a packet from the network.
 *
 * @param[in] el	the event list.
 * @param[in] sockfd	the socket which is ready to read.
 * @param[in] flags	from kevent.
 * @param[in] ctx	the network socket context.
 */
static void fr_network_read(UNUSED fr_event_list_t *el, int sockfd, UNUSED int flags, void *ctx)
{
	int num_messages = 0;
	fr_network_socket_t *s = ctx;
	fr_network_t *nr = s->nr;
	ssize_t data_size;
	fr_channel_data_t *cd, *next;
	fr_time_t *recv_time;

	if (!fr_cond_assert(s->fd == sockfd)) return;

	DEBUG3("network read");

	if (!s->cd) {
		cd = (fr_channel_data_t *) fr_message_reserve(s->ms, s->listen->default_message_size);
		if (!cd) {
			fr_log(nr->log, L_ERR, "Failed allocating message size %zd! - Closing socket", s->listen->default_message_size);
			fr_network_socket_dead(nr, s);
			return;
		}
	} else {
		cd = s->cd;
	}

	rad_assert(cd->m.data != NULL);
	rad_assert(cd->m.rb_size >= 256);

next_message:
	/*
	 *	Poll this socket, but not too often.  We have to go
	 *	service other sockets, too.
	 */
	if (num_messages > 16) {
		s->cd = cd;
		return;
	}

	cd->request.is_dup = false;
	cd->priority = PRIORITY_NORMAL;

	/*
	 *	Read data from the network.
	 *
	 *	Return of 0 means "no data", which is fine for UDP.
	 *	For TCP, if an underlying read() on the TCP socket
	 *	returns 0, (which signals that the FD is no longer
	 *	usable) this function should return -1, so that the
	 *	network side knows that it needs to close the
	 *	connection.
	 */
	data_size = s->listen->app_io->read(s->listen->app_io_instance, &cd->packet_ctx, &recv_time,
					    cd->m.data, cd->m.rb_size, &s->leftover, &cd->priority, &cd->request.is_dup);
	if (data_size == 0) {
		/*
		 *	Cache the message for later.  This is
		 *	important for stream sockets, which can do
		 *	partial reads into the current buffer.  We
		 *	need to be able to give the same buffer back
		 *	to the stream socket for subsequent reads.
		 *
		 *	Since we have a message set for each
		 *	fr_io_socket_t, no "head of line"
		 *	blocking issues can happen for stream sockets.
		 */
		s->cd = cd;
		return;
	}

	/*
	 *	Error: close the connection, and remove the fr_listen_t
	 */
	if (data_size < 0) {
//		fr_log(nr->log, L_DBG_ERR, "error from transport read on socket %d", sockfd);
		fr_network_socket_dead(nr, s);
		return;
	}
	s->cd = NULL;

	DEBUG("Network received packet size %zd", data_size);
	nr->stats.in++;
	s->stats.in++;

	/*
	 *	Initialize the rest of the fields of the channel data.
	 *
	 *	We always use "now" as the time of the message, as the
	 *	packet MAY be a duplicate packet magically resurrected
	 *	from the past.
	 */
	cd->m.when = fr_time();
	cd->listen = s->listen;
	cd->request.recv_time = recv_time;

	/*
	 *	Nothing in the buffer yet.  Allocate room for one
	 *	packet.
	 */
	if ((cd->m.data_size == 0) && (!s->leftover)) {

		(void) fr_message_alloc(s->ms, &cd->m, data_size);
		next = NULL;

	} else {
		/*
		 *	There are leftover bytes in the buffer, feed
		 *	them to the next round of reading.
		 */
		next = (fr_channel_data_t *) fr_message_alloc_reserve(s->ms, &cd->m, data_size, s->leftover,
								      s->listen->default_message_size);
		if (!next) {
			fr_log(nr->log, L_ERR, "Failed reserving partial packet.");
			// @todo - probably close the socket...
			rad_assert(0 == 1);
		}
	}

	if (!fr_network_send_request(nr, cd)) {
		fr_log(nr->log, L_ERR, "Failed sending packet to worker");
		fr_message_done(&cd->m);
		nr->stats.dropped++;
		s->stats.dropped++;
	} else {
		/*
		 *	One more packet sent to a worker.
		 */
		s->outstanding++;
	}

	/*
	 *	If there is a next message, go read it from the buffer.
	 *
	 *	@todo - note that this calls read(), even if the
	 *	app_io has paused the reader.  We likely want to be
	 *	able to check that, too.  We might just remove this
	 *	"goto"...
	 */
	if (next) {
		cd = next;
		num_messages++;
		goto next_message;
	}
}
示例#29
0
static ssize_t redis_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
			  void const *mod_inst, UNUSED void const *xlat_inst,
			  REQUEST *request, char const *fmt)
{
	rlm_redis_t const	*inst = mod_inst;
	fr_redis_conn_t		*conn;

	bool			read_only = false;
	uint8_t	const		*key = NULL;
	size_t			key_len = 0;

	fr_redis_cluster_state_t	state;
	fr_redis_rcode_t		status;
	redisReply		*reply = NULL;
	int			s_ret;

	size_t			len;
	int			ret;

	char const		*p = fmt, *q;

	int			argc;
	char const		*argv[MAX_REDIS_ARGS];
	char			argv_buf[MAX_REDIS_COMMAND_LEN];

	if (p[0] == '-') {
		p++;
		read_only = true;
	}

	/*
	 *	Hack to allow querying against a specific node for testing
	 */
	if (p[0] == '@') {
		fr_socket_addr_t	node_addr;
		fr_pool_t	*pool;

		RDEBUG3("Overriding node selection");

		p++;
		q = strchr(p, ' ');
		if (!q) {
			REDEBUG("Found node specifier but no command, format is [-][@<host>[:port]] <redis command>");
			return -1;
		}

		if (fr_inet_pton_port(&node_addr.ipaddr, &node_addr.port, p, q - p, AF_UNSPEC, true, true) < 0) {
			RPEDEBUG("Failed parsing node address");
			return -1;
		}

		p = q + 1;

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

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

		argc = rad_expand_xlat(request, p, MAX_REDIS_ARGS, argv, false, sizeof(argv_buf), argv_buf);
		if (argc <= 0) {
			RPEDEBUG("Invalid command: %s", p);
		arg_error:
			fr_pool_connection_release(pool, request, conn);
			return -1;
		}
		if (argc >= (MAX_REDIS_ARGS - 1)) {
			RPEDEBUG("Too many parameters; increase MAX_REDIS_ARGS and recompile: %s", p);
			goto arg_error;
		}

		RDEBUG2("Executing command: %s", argv[0]);
		if (argc > 1) {
			RDEBUG2("With argments");
			RINDENT();
			for (int i = 1; i < argc; i++) RDEBUG2("[%i] %s", i, argv[i]);
			REXDENT();
		}

		if (!read_only) {
			reply = redisCommandArgv(conn->handle, argc, argv, NULL);
			status = fr_redis_command_status(conn, reply);
		} else if (redis_command_read_only(&status, &reply, request, conn, argc, argv) == -2) {
			goto close_conn;
		}

		if (!reply) goto fail;

		switch (status) {
		case REDIS_RCODE_SUCCESS:
			goto reply_parse;

		case REDIS_RCODE_RECONNECT:
		close_conn:
			fr_pool_connection_close(pool, request, conn);
			ret = -1;
			goto finish;

		default:
		fail:
			fr_pool_connection_release(pool, request, conn);
			ret = -1;
			goto finish;
		}
	}

	/*
	 *	Normal node selection and execution based on key
	 */
	argc = rad_expand_xlat(request, p, MAX_REDIS_ARGS, argv, false, sizeof(argv_buf), argv_buf);
	if (argc <= 0) {
		RPEDEBUG("Invalid command: %s", p);
		ret = -1;
		goto finish;
	}

	if (argc >= (MAX_REDIS_ARGS - 1)) {
		RPEDEBUG("Too many parameters; increase MAX_REDIS_ARGS and recompile: %s", p);
		ret = -1;
		goto finish;
	}

	/*
	 *	If we've got multiple arguments, the second one is usually the key.
	 *	The Redis docs say commands should be analysed first to get key
	 *	positions, but this involves sending them to the server, which is
	 *	just as expensive as sending them to the wrong server and receiving
	 *	a redirect.
	 */
	if (argc > 1) {
		key = (uint8_t const *)argv[1];
	 	key_len = strlen((char const *)key);
	}
	for (s_ret = fr_redis_cluster_state_init(&state, &conn, inst->cluster, request, key, key_len, read_only);
	     s_ret == REDIS_RCODE_TRY_AGAIN;	/* Continue */
	     s_ret = fr_redis_cluster_state_next(&state, &conn, inst->cluster, request, status, &reply)) {
		RDEBUG2("Executing command: %s", argv[0]);
		if (argc > 1) {
			RDEBUG2("With arguments");
			RINDENT();
			for (int i = 1; i < argc; i++) RDEBUG2("[%i] %s", i, argv[i]);
			REXDENT();
		}
		if (!read_only) {
			reply = redisCommandArgv(conn->handle, argc, argv, NULL);
			status = fr_redis_command_status(conn, reply);
		} else if (redis_command_read_only(&status, &reply, request, conn, argc, argv) == -2) {
			state.close_conn = true;
		}
	}
	if (s_ret != REDIS_RCODE_SUCCESS) {
		ret = -1;
		goto finish;
	}

	if (!fr_cond_assert(reply)) {
		ret = -1;
		goto finish;
	}

reply_parse:
	switch (reply->type) {
	case REDIS_REPLY_INTEGER:
		ret = snprintf(*out, outlen, "%lld", reply->integer);
		break;

	case REDIS_REPLY_STATUS:
	case REDIS_REPLY_STRING:
		len = (((size_t)reply->len) >= outlen) ? outlen - 1: (size_t) reply->len;
		memcpy(*out, reply->str, len);
		(*out)[len] = '\0';
		ret = reply->len;
		break;

	default:
		REDEBUG("Server returned non-value type \"%s\"",
			fr_int2str(redis_reply_types, reply->type, "<UNKNOWN>"));
		ret = -1;
		break;
	}

finish:
	fr_redis_reply_free(reply);
	return ret;
}
示例#30
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;
}