示例#1
0
/** Check group membership attributes to see if a user is a member.
 *
 * @param[in] inst rlm_ldap configuration.
 * @param[in] request Current request.
 * @param[in] check vp containing the group value (name or dn).
 *
 * @return One of the RLM_MODULE_* values.
 */
rlm_rcode_t rlm_ldap_check_cached(ldap_instance_t const *inst, REQUEST *request, VALUE_PAIR *check)
{
	VALUE_PAIR	*vp;
	int		ret;
	vp_cursor_t	cursor;

	fr_cursor_init(&cursor, &request->config_items);

	/*
	 *	We return RLM_MODULE_INVALID here as an indication
	 *	the caller should try a dynamic group lookup instead.
	 */
	vp = fr_cursor_next_by_num(&cursor, inst->cache_da->attr, inst->cache_da->vendor, TAG_ANY);
	if (!vp) return RLM_MODULE_INVALID;
	fr_cursor_first(&cursor);

	while ((vp = fr_cursor_next_by_num(&cursor, inst->cache_da->attr, inst->cache_da->vendor, TAG_ANY))) {
		ret = paircmp_op(T_OP_CMP_EQ, vp, check);
		if (ret == 1) {
			RDEBUG2("User found. Matched cached membership");
			return RLM_MODULE_OK;
		}

		if (ret < -1) {
			return RLM_MODULE_FAIL;
		}
	}

	RDEBUG2("Cached membership not found");
	return RLM_MODULE_NOTFOUND;
}
示例#2
0
/** Check group membership attributes to see if a user is a member.
 *
 * @param[in] inst rlm_ldap configuration.
 * @param[in] request Current request.
 * @param[in] check vp containing the group value (name or dn).
 *
 * @return One of the RLM_MODULE_* values.
 */
rlm_rcode_t rlm_ldap_check_cached(ldap_instance_t const *inst, REQUEST *request, VALUE_PAIR *check)
{
	VALUE_PAIR	*vp;
	int		ret;
	vp_cursor_t	cursor;

	fr_cursor_init(&cursor, &request->config_items);
	vp = fr_cursor_next_by_num(&cursor, inst->cache_da->attr, inst->cache_da->vendor, TAG_ANY);
	if (!vp) {
		return RLM_MODULE_INVALID;
	}

	for (; vp; vp = fr_cursor_next_by_num(&cursor, inst->cache_da->attr, inst->cache_da->vendor, TAG_ANY)) {
		ret = radius_compare_vps(request, check, vp);
		if (ret == 0) {
			RDEBUG2("User found. Matched cached membership");
			return RLM_MODULE_OK;
		}

		if (ret < -1) {
			return RLM_MODULE_FAIL;
		}
	}

	RDEBUG2("Membership not found");
	return RLM_MODULE_NOTFOUND;
}
示例#3
0
/** Find the pair with the matching attribute
 *
 * @todo should take DAs and do a pointer comparison.
 */
VALUE_PAIR *pairfind(VALUE_PAIR *vp, unsigned int attr, unsigned int vendor, int8_t tag)
{
	vp_cursor_t 	cursor;

	/* List head may be NULL if it contains no VPs */
	if (!vp) return NULL;

	VERIFY_LIST(vp);

	(void) fr_cursor_init(&cursor, &vp);
	return fr_cursor_next_by_num(&cursor, attr, vendor, tag);
}
示例#4
0
/** Print out attribute info
 *
 * Prints out all instances of a current attribute, or all attributes in a list.
 *
 * At higher debugging levels, also prints out alternative decodings of the same
 * value. This is helpful to determine types for unknown attributes of long
 * passed vendors, or just crazy/broken NAS.
 *
 * It's also useful for exposing issues in the packet decoding functions, as in
 * some cases they get fed random garbage data.
 *
 * This expands to a zero length string.
 */
static ssize_t xlat_debug_attr(UNUSED void *instance, REQUEST *request, char const *fmt,
			       char *out, UNUSED size_t outlen)
{
	VALUE_PAIR *vp, **vps;
	REQUEST *current;
	value_pair_tmpl_t vpt;
	vp_cursor_t cursor;
	char buffer[1024];

	if (!RDEBUG_ENABLED2) {
		*out = '\0';
		return -1;
	}

	while (isspace((int) *fmt)) fmt++;
	if (*fmt == '&') fmt++;

	if (radius_parse_attr(fmt, &vpt, REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) {
		return -1;
	}

	current = request;
	if (radius_request(&current, vpt.request) < 0) return -2;

	vps = radius_list(current, vpt.list);
	if (!vps) {
		return -2;
	}

	RIDEBUG("Attributes matching \"%s\"", fmt);
	vp = fr_cursor_init(&cursor, vps);

	if (vpt.da) {
		vp = fr_cursor_next_by_num(&cursor, vpt.da->attr, vpt.da->vendor, TAG_ANY);
	}
	while (vp) {
		DICT_ATTR *dac = NULL;
		DICT_VENDOR *dv;
		VALUE_PAIR *vpc = NULL;
		FR_NAME_NUMBER const *type;

		vp_prints_value(buffer, sizeof(buffer), vp, '\'');

		if (vp->da->flags.has_tag) {
			RIDEBUG2("\t%s:%s:%i %s %s",
				fr_int2str(pair_lists, vpt.list, "<INVALID>"),
				vp->da->name,
				vp->tag,
				fr_int2str(fr_tokens, vp->op, "<INVALID>"),
				buffer);
		} else {
			RIDEBUG2("\t%s:%s %s %s",
				fr_int2str(pair_lists, vpt.list, "<INVALID>"),
				vp->da->name,
				fr_int2str(fr_tokens, vp->op, "<INVALID>"),
				buffer);
		}

		if (!RDEBUG_ENABLED3) {
			goto next_vp;
		}

		if (vp->da->vendor) {
			dv = dict_vendorbyvalue(vp->da->vendor);
			RDEBUG3("\t\tvendor        : %i (%s)", vp->da->vendor, dv ? dv->name : "unknown");
		}
		RDEBUG3("\t\ttype          : %s", fr_int2str(dict_attr_types, vp->da->type, "<INVALID>"));
		RDEBUG3("\t\tlength        : %zu", vp->length);

		dac = talloc_memdup(request, vp->da, sizeof(DICT_ATTR));
		if (!dac) {
			return -1;
		}
		dac->flags.vp_free = 0;

		if (!RDEBUG_ENABLED4) {
			goto next_vp;
		}

		type = dict_attr_types;
		while (type->name) {
			int pad;
			ssize_t len;
			uint8_t const *data = NULL;
			vpc = NULL;

			if ((PW_TYPE) type->number == vp->da->type) {
				goto next_type;
			}

			switch (type->number) {
				case PW_TYPE_INVALID:		/* Not real type */
				case PW_TYPE_MAX:		/* Not real type */
				case PW_TYPE_EXTENDED:		/* Not safe/appropriate */
				case PW_TYPE_LONG_EXTENDED:	/* Not safe/appropriate */
				case PW_TYPE_TLV:		/* Not safe/appropriate */
				case PW_TYPE_VSA:		/* @fixme We need special behaviour for these */
					goto next_type;
				default:
					break;
			}

			dac->type = type->number;
			len = rad_vp2data(&data, vp);
			if (len < 0) {
				goto next_type;
			}
			if (data2vp(NULL, NULL, NULL, dac, data, len, len, &vpc) < 0) {
				goto next_type;
			}

			/*
			 *	data2vp has knowledge of expected format lengths, if the length
			 *	from rad_vp2data doesn't match, it encodes the attribute
			 *	as raw octets. This results in many useless debug lines with
			 *	the same hex string.
			 */
			if ((type->number != PW_TYPE_OCTETS) && (vpc->da->type == PW_TYPE_OCTETS)) {
				goto next_type;
			}

			if (!vp_prints_value(buffer, sizeof(buffer), vpc, '\'')) {
				goto next_type;
			}

			if ((pad = (11 - strlen(type->name))) < 0) {
				pad = 0;
			}

			/*
			 *	@fixme: if the value happens to decode as a VSA
			 *	(someone put a VSA into a VSA?), we probably to print
			 *	extended info for that/reparse
			 */
			RDEBUG4("\t\tas %s%*s: %s", type->name, pad, " ", buffer);

			next_type:
			talloc_free(vpc);
			type++;
		}
		next_vp:

		talloc_free(dac);

		if (vpt.da) {
			vp = fr_cursor_next_by_num(&cursor, vpt.da->attr, vpt.da->vendor, TAG_ANY);
		} else {
			vp = fr_cursor_next(&cursor);
		}
	}

	*out = '\0';
	return 0;
}
示例#5
0
static char *xlat_getvp(TALLOC_CTX *ctx, REQUEST *request, pair_lists_t list, DICT_ATTR const *da, int8_t tag,
			int num, bool return_null)
{
	VALUE_PAIR *vp, *vps = NULL;
	RADIUS_PACKET *packet = NULL;
	DICT_VALUE *dv;
	VALUE_PAIR myvp;

	/*
	 *	Arg.  Too much abstraction is annoying.
	 */
	switch (list) {
	default:
		if (return_null) return NULL;
		return vp_aprinttype(ctx, da->type);

	case PAIR_LIST_CONTROL:
		vps = request->config_items;
		break;

	case PAIR_LIST_REQUEST:
		packet = request->packet;
		if (packet) vps = packet->vps;
		break;

	case PAIR_LIST_REPLY:
		packet = request->reply;
		if (packet) vps = packet->vps;
		break;

#ifdef WITH_PROXY
	case PAIR_LIST_PROXY_REQUEST:
		packet = request->proxy;
		if (packet) vps = packet->vps;
		break;

	case PAIR_LIST_PROXY_REPLY:
		packet = request->proxy_reply;
		if (packet) vps = packet->vps;
		break;
#endif

#ifdef WITH_COA
	case PAIR_LIST_COA:
	case PAIR_LIST_DM:
		if (request->coa) packet = request->coa->packet;
		if (packet) vps = packet->vps;
		break;

	case PAIR_LIST_COA_REPLY:
	case PAIR_LIST_DM_REPLY:
		if (request->coa) packet = request->coa->reply;
		if (packet) vps = packet->vps;
		break;

#endif
	}

	/*
	 *	Now that we have the list, etc. handled,
	 *	find the VP and print it.
	 */
	if ((da->vendor != 0) || (da->attr < 256) || (list == PAIR_LIST_CONTROL)) {
	print_vp:
		vp = pairfind(vps, da->attr, da->vendor, tag);
		if (!vp) {
			return NULL;
		}
		goto do_print;
	}

	/*
	 *	Some non-packet expansions
	 */
	switch (da->attr) {
	default:
		break;		/* ignore them */

	case PW_CLIENT_SHORTNAME:
		if (request->client && request->client->shortname) {
			return talloc_strdup(ctx, request->client->shortname);
		}
		return talloc_strdup(ctx, "<UNKNOWN-CLIENT>");

	case PW_REQUEST_PROCESSING_STAGE:
		if (request->component) {
			return talloc_strdup(ctx, request->component);
		}
		return talloc_strdup(ctx, "server_core");

	case PW_VIRTUAL_SERVER:
		if (!request->server) return NULL;
		return talloc_strdup(ctx, request->server);

	case PW_MODULE_RETURN_CODE:
		return talloc_asprintf(ctx, "%d", request->simul_max); /* hack */
	}

	/*
	 *	All of the attributes must now refer to a packet.  If
	 *	there's no packet, we can't print any attribute
	 *	referencing it.
	 */
	if (!packet) {
		if (return_null) return NULL;
		return vp_aprinttype(ctx, da->type);
	}

	memset(&myvp, 0, sizeof(myvp));
	myvp.da = da;
	vp = NULL;

	switch (da->attr) {
	default:
		goto print_vp;

	case PW_PACKET_TYPE:
		dv = dict_valbyattr(PW_PACKET_TYPE, 0, packet->code);
		if (dv) return talloc_strdup(ctx, dv->name);
		return talloc_asprintf(ctx, "%d", packet->code);

	case PW_RESPONSE_PACKET_TYPE:
	{
		int code = 0;

#ifdef WITH_PROXY
		if (request->proxy_reply && (!request->reply || !request->reply->code)) {
			code = request->proxy_reply->code;
		} else
#endif
			if (request->reply) {
				code = request->reply->code;
			}

		return talloc_strdup(ctx, fr_packet_codes[code]);
	}

	case PW_PACKET_AUTHENTICATION_VECTOR:
		myvp.length = sizeof(packet->vector);
		memcpy(&myvp.vp_octets, packet->vector, sizeof(packet->vector));
		vp = &myvp;
		break;

	case PW_CLIENT_IP_ADDRESS:
	case PW_PACKET_SRC_IP_ADDRESS:
		if (packet->src_ipaddr.af == AF_INET) {
			myvp.vp_ipaddr = packet->src_ipaddr.ipaddr.ip4addr.s_addr;
			vp = &myvp;
		}
		break;

	case PW_PACKET_DST_IP_ADDRESS:
		if (packet->dst_ipaddr.af == AF_INET) {
			myvp.vp_ipaddr = packet->dst_ipaddr.ipaddr.ip4addr.s_addr;
			vp = &myvp;
		}
		break;

	case PW_PACKET_SRC_IPV6_ADDRESS:
		if (packet->src_ipaddr.af == AF_INET6) {
			memcpy(&myvp.vp_ipv6addr,
			       &packet->src_ipaddr.ipaddr.ip6addr,
			       sizeof(packet->src_ipaddr.ipaddr.ip6addr));
			vp = &myvp;
		}
		break;

	case PW_PACKET_DST_IPV6_ADDRESS:
		if (packet->dst_ipaddr.af == AF_INET6) {
			memcpy(&myvp.vp_ipv6addr,
			       &packet->dst_ipaddr.ipaddr.ip6addr,
			       sizeof(packet->dst_ipaddr.ipaddr.ip6addr));
			vp = &myvp;
		}
		break;

	case PW_PACKET_SRC_PORT:
		myvp.vp_integer = packet->src_port;
		vp = &myvp;
		break;

	case PW_PACKET_DST_PORT:
		myvp.vp_integer = packet->dst_port;
		vp = &myvp;
		break;
	}

do_print:
	/*
	 *	Hack up the virtual attributes.
	 */
	if (num && (vp == &myvp)) {
		char *p, *q;

		/*
		 *	[*] means only one.
		 */
		if (num == 65537) num = 0;

		/*
		 *	[n] means NULL, as there's only one.
		 */
		if ((num > 0) && (num < 65536)) {
			return NULL;
		}

		p = vp_aprint(ctx, vp);
		rad_assert(p != NULL);

		/*
		 *	Get the length of it.
		 */
		if (num == 65536) {
			q = talloc_asprintf(ctx, "%d", (int) strlen(p));
			talloc_free(p);
			return q;
		}

		return p;
	}

	/*
	 *	We want the N'th VP.
	 */
	if (num) {
		int count = 0;
		vp_cursor_t cursor;

		/*
		 *	Return a count of the VPs.
		 */
		if (num == 65536) {
			fr_cursor_init(&cursor, &vp);
			while (fr_cursor_next_by_num(&cursor, da->attr, da->vendor, tag) != NULL) {
				count++;
			}

			return talloc_asprintf(ctx, "%d", count);
		}

		/*
		 *	Ugly, but working.
		 */
		if (num == 65537) {
			char *p, *q;

			(void) fr_cursor_init(&cursor, &vp);
			vp = fr_cursor_next_by_num(&cursor, da->attr, da->vendor, tag);
			if (!vp) return NULL;
			p = vp_aprint(ctx, vp);
			while ((vp = fr_cursor_next_by_num(&cursor, da->attr, da->vendor, tag)) != NULL) {
				q = vp_aprint(ctx, vp);
				p = talloc_strdup_append(p, ",");
				p = talloc_strdup_append(p, q);
			}

			return p;
		}

		(void) fr_cursor_init(&cursor, &vp);
		while ((vp = fr_cursor_next_by_num(&cursor, da->attr, da->vendor, tag)) != NULL) {
			if (count == num) {
				break;
			}
			count++;
		}
	}

	if (!vp) {
		if (return_null) return NULL;
		return vp_aprinttype(ctx, da->type);
	}

	return vp_aprint(ctx, vp);
}
示例#6
0
/** Start a process
 *
 * @param cmd Command to execute. This is parsed into argv[] parts, then each individual argv
 *	part is xlat'ed.
 * @param request Current reuqest
 * @param exec_wait set to true to read from or write to child.
 * @param[in,out] input_fd pointer to int, receives the stdin file descriptor. Set to NULL
 *	and the child will have /dev/null on stdin.
 * @param[in,out] output_fd pinter to int, receives the stdout file descriptor. Set to NULL
 *	and child will have /dev/null on stdout.
 * @param input_pairs list of value pairs - these will be put into the environment variables
 *	of the child.
 * @param shell_escape values before passing them as arguments.
 * @return
 *	- PID of the child process.
 *	- -1 on failure.
 */
pid_t radius_start_program(char const *cmd, REQUEST *request, bool exec_wait,
			   int *input_fd, int *output_fd,
			   VALUE_PAIR *input_pairs, bool shell_escape)
{
#ifndef __MINGW32__
	char		*p;
	VALUE_PAIR	*vp;
	int		n;
	int		to_child[2] = {-1, -1};
	int		from_child[2] = {-1, -1};
	pid_t		pid;
#endif
	int		argc;
	int		i;
	char const	**argv_p;
	char		*argv[MAX_ARGV], **argv_start = argv;
	char		argv_buf[4096];
#define MAX_ENVP 1024
	char		*envp[MAX_ENVP];
	size_t		envlen = 0;
	TALLOC_CTX	*input_ctx = NULL;

	/*
	 *	Stupid array decomposition...
	 *
	 *	If we do memcpy(&argv_p, &argv, sizeof(argv_p)) src ends up being a char **
	 *	pointing to the value of the first element.
	 */
	memcpy(&argv_p, &argv_start, sizeof(argv_p));
	argc = rad_expand_xlat(request, cmd, MAX_ARGV, argv_p, true, sizeof(argv_buf), argv_buf);
	if (argc <= 0) {
		ERROR("Invalid command '%s'", cmd);
		return -1;
	}

	if (DEBUG_ENABLED3) {
		for (i = 0; i < argc; i++) DEBUG3("arg[%d] %s", i, argv[i]);
	}

#ifndef __MINGW32__
	/*
	 *	Open a pipe for child/parent communication, if necessary.
	 */
	if (exec_wait) {
		if (input_fd) {
			if (pipe(to_child) != 0) {
				ERROR("Couldn't open pipe to child: %s", fr_syserror(errno));
				return -1;
			}
		}
		if (output_fd) {
			if (pipe(from_child) != 0) {
				ERROR("Couldn't open pipe from child: %s", fr_syserror(errno));
				/* safe because these either need closing or are == -1 */
				close(to_child[0]);
				close(to_child[1]);
				return -1;
			}
		}
	}

	envp[0] = NULL;

	if (input_pairs) {
		vp_cursor_t cursor;
		char buffer[1024];

		input_ctx = talloc_new(request);

		/*
		 *	Set up the environment variables in the
		 *	parent, so we don't call libc functions that
		 *	hold mutexes.  They might be locked when we fork,
		 *	and will remain locked in the child.
		 */
		for (vp = fr_cursor_init(&cursor, &input_pairs);
		     vp && (envlen < ((sizeof(envp) / sizeof(*envp)) - 1));
		     vp = fr_cursor_next(&cursor)) {
			/*
			 *	Hmm... maybe we shouldn't pass the
			 *	user's password in an environment
			 *	variable...
			 */
			snprintf(buffer, sizeof(buffer), "%s=", vp->da->name);
			if (shell_escape) {
				for (p = buffer; *p != '='; p++) {
					if (*p == '-') {
						*p = '_';
					} else if (isalpha((int) *p)) {
						*p = toupper(*p);
					}
				}
			}

			n = strlen(buffer);
			fr_pair_value_snprint(buffer + n, sizeof(buffer) - n, vp, shell_escape ? '"' : 0);

			DEBUG3("export %s", buffer);
			envp[envlen++] = talloc_strdup(input_ctx, buffer);
		}

		fr_cursor_init(&cursor, radius_list(request, PAIR_LIST_CONTROL));
		while ((envlen < ((sizeof(envp) / sizeof(*envp)) - 1)) &&
		       (vp = fr_cursor_next_by_num(&cursor, 0, PW_EXEC_EXPORT, TAG_ANY))) {
			DEBUG3("export %s", vp->vp_strvalue);
			memcpy(&envp[envlen++], &vp->vp_strvalue, sizeof(*envp));
		}

		/*
		 *	NULL terminate for execve
		 */
		envp[envlen] = NULL;
	}

	if (exec_wait) {
		pid = rad_fork();	/* remember PID */
	} else {
		pid = fork();		/* don't wait */
	}

	if (pid == 0) {
		int devnull;

		/*
		 *	Child process.
		 *
		 *	We try to be fail-safe here. So if ANYTHING
		 *	goes wrong, we exit with status 1.
		 */

		/*
		 *	Open STDIN to /dev/null
		 */
		devnull = open("/dev/null", O_RDWR);
		if (devnull < 0) {
			ERROR("Failed opening /dev/null: %s\n", fr_syserror(errno));

			/*
			 *	Where the status code is interpreted as a module rcode
			 * 	one is subtracted from it, to allow 0 to equal success
			 *
			 *	2 is RLM_MODULE_FAIL + 1
			 */
			exit(2);
		}

		/*
		 *	Only massage the pipe handles if the parent
		 *	has created them.
		 */
		if (exec_wait) {
			if (input_fd) {
				close(to_child[1]);
				dup2(to_child[0], STDIN_FILENO);
			} else {
				dup2(devnull, STDIN_FILENO);
			}

			if (output_fd) {
				close(from_child[0]);
				dup2(from_child[1], STDOUT_FILENO);
			} else {
				dup2(devnull, STDOUT_FILENO);
			}

		} else {	/* no pipe, STDOUT should be /dev/null */
			dup2(devnull, STDIN_FILENO);
			dup2(devnull, STDOUT_FILENO);
		}

		/*
		 *	If we're not debugging, then we can't do
		 *	anything with the error messages, so we throw
		 *	them away.
		 *
		 *	If we are debugging, then we want the error
		 *	messages to go to the STDERR of the server.
		 */
		if (rad_debug_lvl == 0) {
			dup2(devnull, STDERR_FILENO);
		}
		close(devnull);

		/*
		 *	The server may have MANY FD's open.  We don't
		 *	want to leave dangling FD's for the child process
		 *	to play funky games with, so we close them.
		 */
		closefrom(3);

		/*
		 *	I swear the signature for execve is wrong and should
		 *	take 'char const * const argv[]'.
		 *
		 *	Note: execve(), unlike system(), treats all the space
		 *	delimited arguments as literals, so there's no need
		 *	to perform additional escaping.
		 */
		execve(argv[0], argv, envp);
		printf("Failed to execute \"%s\": %s", argv[0], fr_syserror(errno)); /* fork output will be captured */

		/*
		 *	Where the status code is interpreted as a module rcode
		 * 	one is subtracted from it, to allow 0 to equal success
		 *
		 *	2 is RLM_MODULE_FAIL + 1
		 */
		exit(2);
	}

	/*
	 *	Free child environment variables
	 */
	talloc_free(input_ctx);

	/*
	 *	Parent process.
	 */
	if (pid < 0) {
		ERROR("Couldn't fork %s: %s", argv[0], fr_syserror(errno));
		if (exec_wait) {
			/* safe because these either need closing or are == -1 */
			close(to_child[0]);
			close(to_child[1]);
			close(from_child[0]);
			close(from_child[1]);
		}
		return -1;
	}

	/*
	 *	We're not waiting, exit, and ignore any child's status.
	 */
	if (exec_wait) {
		/*
		 *	Close the ends of the pipe(s) the child is using
		 *	return the ends of the pipe(s) our caller wants
		 *
		 */
		if (input_fd) {
			*input_fd = to_child[1];
			close(to_child[0]);
		}
		if (output_fd) {
			*output_fd = from_child[0];
			close(from_child[1]);
		}
	}

	return pid;
#else
	if (exec_wait) {
		ERROR("Wait is not supported");
		return -1;
	}

	{
		/*
		 *	The _spawn and _exec families of functions are
		 *	found in Windows compiler libraries for
		 *	portability from UNIX. There is a variety of
		 *	functions, including the ability to pass
		 *	either a list or array of parameters, to
		 *	search in the PATH or otherwise, and whether
		 *	or not to pass an environment (a set of
		 *	environment variables). Using _spawn, you can
		 *	also specify whether you want the new process
		 *	to close your program (_P_OVERLAY), to wait
		 *	until the new process is finished (_P_WAIT) or
		 *	for the two to run concurrently (_P_NOWAIT).

		 *	_spawn and _exec are useful for instances in
		 *	which you have simple requirements for running
		 *	the program, don't want the overhead of the
		 *	Windows header file, or are interested
		 *	primarily in portability.
		 */

		/*
		 *	FIXME: check return code... what is it?
		 */
		_spawnve(_P_NOWAIT, argv[0], argv, envp);
	}

	return 0;
#endif
}
示例#7
0
/*
 * Handles multiple EAP-Message attrs
 * ie concatenates all to get the complete EAP packet.
 *
 * NOTE: Sometimes Framed-MTU might contain the length of EAP-Message,
 *      refer fragmentation in rfc2869.
 */
eap_packet_raw_t *eap_vp2packet(TALLOC_CTX *ctx, VALUE_PAIR *vps)
{
	VALUE_PAIR *first, *i;
	eap_packet_raw_t *eap_packet;
	unsigned char *ptr;
	uint16_t len;
	int total_len;
	vp_cursor_t cursor;

	/*
	 *	Get only EAP-Message attribute list
	 */
	first = fr_pair_find_by_num(vps, 0, PW_EAP_MESSAGE, TAG_ANY);
	if (!first) {
		fr_strerror_printf("EAP-Message not found");
		return NULL;
	}

	/*
	 *	Sanity check the length before doing anything.
	 */
	if (first->vp_length < 4) {
		fr_strerror_printf("EAP packet is too short");
		return NULL;
	}

	/*
	 *	Get the Actual length from the EAP packet
	 *	First EAP-Message contains the EAP packet header
	 */
	memcpy(&len, first->vp_strvalue + 2, sizeof(len));
	len = ntohs(len);

	/*
	 *	Take out even more weird things.
	 */
	if (len < 4) {
		fr_strerror_printf("EAP packet has invalid length (less than 4 bytes)");
		return NULL;
	}

	/*
	 *	Sanity check the length, BEFORE allocating  memory.
	 */
	total_len = 0;
	fr_cursor_init(&cursor, &first);
	while ((i = fr_cursor_next_by_num(&cursor, 0, PW_EAP_MESSAGE, TAG_ANY))) {
		total_len += i->vp_length;

		if (total_len > len) {
			fr_strerror_printf("Malformed EAP packet.  Length in packet header %i, "
					   "does not match actual length %i", len, total_len);
			return NULL;
		}
	}

	/*
	 *	If the length is SMALLER, die, too.
	 */
	if (total_len < len) {
		fr_strerror_printf("Malformed EAP packet.  Length in packet header does not "
				   "match actual length");
		return NULL;
	}

	/*
	 *	Now that we know the lengths are OK, allocate memory.
	 */
	eap_packet = (eap_packet_raw_t *) talloc_zero_array(ctx, uint8_t, len);
	if (!eap_packet) {
		return NULL;
	}

	/*
	 *	Copy the data from EAP-Message's over to our EAP packet.
	 */
	ptr = (unsigned char *)eap_packet;

	/* RADIUS ensures order of attrs, so just concatenate all */
	fr_cursor_first(&cursor);
	while ((i = fr_cursor_next_by_num(&cursor, 0, PW_EAP_MESSAGE, TAG_ANY))) {
		memcpy(ptr, i->vp_strvalue, i->vp_length);
		ptr += i->vp_length;
	}

	return eap_packet;
}
示例#8
0
/*
 *	Check password.
 *
 *	Returns:	0  OK
 *			-1 Password fail
 *			-2 Rejected (Auth-Type = Reject, send Port-Message back)
 *			1  End check & return, don't reply
 *
 *	NOTE: NOT the same as the RLM_ values !
 */
static int CC_HINT(nonnull) rad_check_password(REQUEST *request)
{
	vp_cursor_t cursor;
	VALUE_PAIR *auth_type_pair;
	int auth_type = -1;
	int result;
	int auth_type_count = 0;

	/*
	 *	Look for matching check items. We skip the whole lot
	 *	if the authentication type is PW_AUTHTYPE_ACCEPT or
	 *	PW_AUTHTYPE_REJECT.
	 */
	fr_cursor_init(&cursor, &request->config);
	while ((auth_type_pair = fr_cursor_next_by_num(&cursor, PW_AUTH_TYPE, 0, TAG_ANY))) {
		auth_type = auth_type_pair->vp_integer;
		auth_type_count++;

		RDEBUG2("Found Auth-Type = %s", dict_valnamebyattr(PW_AUTH_TYPE, 0, auth_type));
		if (auth_type == PW_AUTHTYPE_REJECT) {
			RDEBUG2("Auth-Type = Reject, rejecting user");

			return -2;
		}
	}

	/*
	 *	Warn if more than one Auth-Type was found, because only the last
	 *	one found will actually be used.
	 */
	if ((auth_type_count > 1) && (debug_flag)) {
		RERROR("Warning:  Found %d auth-types on request for user '%s'",
			auth_type_count, request->username->vp_strvalue);
	}

	/*
	 *	This means we have a proxy reply or an accept and it wasn't
	 *	rejected in the above loop. So that means it is accepted and we
	 *	do no further authentication.
	 */
	if ((auth_type == PW_AUTHTYPE_ACCEPT)
#ifdef WITH_PROXY
	    || (request->proxy)
#endif
	    ) {
		RDEBUG2("Auth-Type = Accept, accepting the user");
		return 0;
	}

	/*
	 *	Check that Auth-Type has been set, and reject if not.
	 *
	 *	Do quick checks to see if Cleartext-Password or Crypt-Password have
	 *	been set, and complain if so.
	 */
	if (auth_type < 0) {
		if (pairfind(request->config, PW_CRYPT_PASSWORD, 0, TAG_ANY) != NULL) {
			RWDEBUG2("Please update your configuration, and remove 'Auth-Type = Crypt'");
			RWDEBUG2("Use the PAP module instead");
		}
		else if (pairfind(request->config, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY) != NULL) {
			RWDEBUG2("Please update your configuration, and remove 'Auth-Type = Local'");
			RWDEBUG2("Use the PAP or CHAP modules instead");
		}

		/*
		 *	The admin hasn't told us how to
		 *	authenticate the user, so we reject them!
		 *
		 *	This is fail-safe.
		 */

		REDEBUG2("No Auth-Type found: rejecting the user via Post-Auth-Type = Reject");
		return -2;
	}

	/*
	 *	See if there is a module that handles
	 *	this Auth-Type, and turn the RLM_ return
	 *	status into the values as defined at
	 *	the top of this function.
	 */
	result = process_authenticate(auth_type, request);
	switch (result) {
	/*
	 *	An authentication module FAIL
	 *	return code, or any return code that
	 *	is not expected from authentication,
	 *	is the same as an explicit REJECT!
	 */
	case RLM_MODULE_FAIL:
	case RLM_MODULE_INVALID:
	case RLM_MODULE_NOOP:
	case RLM_MODULE_NOTFOUND:
	case RLM_MODULE_REJECT:
	case RLM_MODULE_UPDATED:
	case RLM_MODULE_USERLOCK:
	default:
		result = -1;
		break;

	case RLM_MODULE_OK:
		result = 0;
		break;

	case RLM_MODULE_HANDLED:
		result = 1;
		break;
	}

	return result;
}
/** Copy packet to multiple servers
 *
 * Create a duplicate of the packet and send it to a list of realms
 * defined by the presence of the Replicate-To-Realm VP in the control
 * list of the current request.
 *
 * This is pretty hacky and is 100% fire and forget. If you're looking
 * to forward authentication requests to multiple realms and process
 * the responses, this function will not allow you to do that.
 *
 * @param[in] instance 	of this module.
 * @param[in] request 	The current request.
 * @param[in] list	of attributes to copy to the duplicate packet.
 * @param[in] code	to write into the code field of the duplicate packet.
 * @return RCODE fail on error, invalid if list does not exist, noop if no replications succeeded, else ok.
 */
static rlm_rcode_t replicate_packet(UNUSED void *instance, REQUEST *request, pair_lists_t list, PW_CODE code)
{
	int rcode = RLM_MODULE_NOOP;
	bool pass1 = true;

	vp_cursor_t cursor;
	VALUE_PAIR *vp;

	RADIUS_PACKET *packet = NULL;

	rcode = rlm_replicate_alloc(&packet, request, list, code);
	if (rcode != RLM_MODULE_OK) {
		return rcode;
	}

	/*
	 *	Send as many packets as necessary to different destinations.
	 */
	fr_cursor_init(&cursor, &request->config_items);
	while ((vp = fr_cursor_next_by_num(&cursor, PW_REPLICATE_TO_REALM, 0, TAG_ANY))) {
		home_server_t *home;
		REALM *realm;
		home_pool_t *pool;

		realm = realm_find2(vp->vp_strvalue);
		if (!realm) {
			REDEBUG2("Cannot Replicate to unknown realm \"%s\"", vp->vp_strvalue);
			continue;
		}

		/*
		 *	We shouldn't really do this on every loop.
		 */
		switch (request->packet->code) {
		default:
			REDEBUG2("Cannot replicate unknown packet code %d", request->packet->code);
			rcode = RLM_MODULE_FAIL;
			goto done;

		case PW_CODE_ACCESS_REQUEST:
			pool = realm->auth_pool;
			break;

#ifdef WITH_ACCOUNTING

		case PW_CODE_ACCOUNTING_REQUEST:
			pool = realm->acct_pool;
			break;
#endif

#ifdef WITH_COA
		case PW_CODE_COA_REQUEST:
		case PW_CODE_DISCONNECT_REQUEST:
			pool = realm->acct_pool;
			break;
#endif
		}

		if (!pool) {
			RWDEBUG2("Cancelling replication to Realm %s, as the realm is local", realm->name);
			continue;
		}

		home = home_server_ldb(realm->name, pool, request);
		if (!home) {
			REDEBUG2("Failed to find live home server for realm %s", realm->name);
			continue;
		}

		/*
		 *	For replication to multiple servers we re-use the packet
		 *	we built here.
		 */
		if (pass1) {
			packet->id = fr_rand() & 0xff;
			packet->sockfd = fr_socket(&home->src_ipaddr, 0);
			if (packet->sockfd < 0) {
				REDEBUG("Failed opening socket: %s", fr_strerror());
				rcode = RLM_MODULE_FAIL;
				goto done;
			}
			pass1 = false;
		} else {
			size_t i;

			for (i = 0; i < sizeof(packet->vector); i++) {
				packet->vector[i] = fr_rand() & 0xff;
			}

			packet->id++;
			TALLOC_FREE(packet->data);
			packet->data_len = 0;
		}

		/*
		 *	(Re)-Write these.
		 */
		packet->dst_ipaddr = home->ipaddr;
		packet->dst_port = home->port;
		memset(&packet->src_ipaddr, 0, sizeof(packet->src_ipaddr));
		packet->src_port = 0;

		/*
		 *	Encode, sign and then send the packet.
		 */
		RDEBUG("Replicating list '%s' to Realm '%s'", fr_int2str(pair_lists, list, "<INVALID>"), realm->name);
		if (rad_send(packet, NULL, home->secret) < 0) {
			REDEBUG("Failed replicating packet: %s", fr_strerror());
			rcode = RLM_MODULE_FAIL;
			goto done;
		}

		/*
		 *	We've sent it to at least one destination.
		 */
		rcode = RLM_MODULE_OK;
	}

	done:

	talloc_free(packet);
	return rcode;
}
示例#10
0
/*
 *	Initialize a radclient data structure and add it to
 *	the global linked list.
 */
static int radclient_init(TALLOC_CTX *ctx, rc_file_pair_t *files)
{
	FILE *packets, *filters = NULL;

	vp_cursor_t cursor;
	VALUE_PAIR *vp;
	rc_request_t *request;
	bool packets_done = false;
	uint64_t num = 0;

	assert(files->packets != NULL);

	/*
	 *	Determine where to read the VP's from.
	 */
	if (strcmp(files->packets, "-") != 0) {
		packets = fopen(files->packets, "r");
		if (!packets) {
			ERROR("Error opening %s: %s", files->packets, strerror(errno));
			return 0;
		}

		/*
		 *	Read in the pairs representing the expected response.
		 */
		if (files->filters) {
			filters = fopen(files->filters, "r");
			if (!filters) {
				ERROR("Error opening %s: %s", files->filters, strerror(errno));
				fclose(packets);
				return 0;
			}
		}
	} else {
		packets = stdin;
	}


	/*
	 *	Loop until the file is done.
	 */
	do {
		/*
		 *	Allocate it.
		 */
		request = talloc_zero(ctx, rc_request_t);
		if (!request) {
			ERROR("Out of memory");
			goto error;
		}
		talloc_set_destructor(request, _rc_request_free);

		request->packet = rad_alloc(request, 1);
		if (!request->packet) {
			ERROR("Out of memory");
			goto error;
		}

#ifdef WITH_TCP
		request->packet->src_ipaddr = client_ipaddr;
		request->packet->src_port = client_port;
		request->packet->dst_ipaddr = server_ipaddr;
		request->packet->dst_port = server_port;
		request->packet->proto = ipproto;
#endif

		request->files = files;
		request->packet->id = -1; /* allocate when sending */
		request->num = num++;

		/*
		 *	Read the request VP's.
		 */
		if (readvp2(&request->packet->vps, request->packet, packets, &packets_done) < 0) {
			ERROR("Error parsing \"%s\"", files->packets);
			goto error;
		}

		fr_cursor_init(&cursor, &request->filter);
		vp = fr_cursor_next_by_num(&cursor, PW_PACKET_TYPE, 0, TAG_ANY);
		if (vp) {
			fr_cursor_remove(&cursor);
			request->packet_code = vp->vp_integer;
			talloc_free(vp);
		} else {
			request->packet_code = packet_code; /* Use the default set on the command line */
		}

		/*
		 *	Read in filter VP's.
		 */
		if (filters) {
			bool filters_done;

			if (readvp2(&request->filter, request, filters, &filters_done) < 0) {
				ERROR("Error parsing \"%s\"", files->filters);
				goto error;
			}

			if (!request->filter) {
				goto error;
			}

			if (filters_done && !packets_done) {
				ERROR("Differing number of packets/filters in %s:%s "
				      "(too many requests))", files->packets, files->filters);
				goto error;
			}

			if (!filters_done && packets_done) {
				ERROR("Differing number of packets/filters in %s:%s "
				      "(too many filters))", files->packets, files->filters);
				goto error;
			}

			fr_cursor_init(&cursor, &request->filter);
			vp = fr_cursor_next_by_num(&cursor, PW_PACKET_TYPE, 0, TAG_ANY);
			if (vp) {
				fr_cursor_remove(&cursor);
				request->filter_code = vp->vp_integer;
				talloc_free(vp);
			}

			/*
			 *	xlat expansions aren't supported here
			 */
			for (vp = fr_cursor_init(&cursor, &request->filter);
			     vp;
			     vp = fr_cursor_next(&cursor)) {
				if (vp->type == VT_XLAT) {
					vp->type = VT_DATA;
					vp->vp_strvalue = vp->value.xlat;
				}
			}

			/*
			 *	This allows efficient list comparisons later
			 */
			pairsort(&request->filter, attrtagcmp);
		}

		/*
		 *	Determine the response code from the request (if not already set)
		 */
		if (!request->filter_code) {
			switch (request->packet_code) {
			case PW_CODE_AUTHENTICATION_REQUEST:
				request->filter_code = PW_CODE_AUTHENTICATION_ACK;
				break;

			case PW_CODE_ACCOUNTING_REQUEST:
				request->filter_code = PW_CODE_ACCOUNTING_RESPONSE;
				break;

			case PW_CODE_COA_REQUEST:
				request->filter_code = PW_CODE_COA_ACK;
				break;

			case PW_CODE_DISCONNECT_REQUEST:
				request->filter_code = PW_CODE_DISCONNECT_ACK;
				break;

			default:
				break;
			}
		}

		/*
		 *	Keep a copy of the the User-Password attribute.
		 */
		if ((vp = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) {
			strlcpy(request->password, vp->vp_strvalue,
				sizeof(request->password));
		/*
		 *	Otherwise keep a copy of the CHAP-Password attribute.
		 */
		} else if ((vp = pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
			strlcpy(request->password, vp->vp_strvalue,
				sizeof(request->password));

		} else if ((vp = pairfind(request->packet->vps, PW_MSCHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
			strlcpy(request->password, vp->vp_strvalue,
				sizeof(request->password));
		} else {
			request->password[0] = '\0';
		}

		/*
		 *	Fix up Digest-Attributes issues
		 */
		for (vp = fr_cursor_init(&cursor, &request->packet->vps);
		     vp;
		     vp = fr_cursor_next(&cursor)) {
			/*
			 *	Double quoted strings get marked up as xlat expansions,
			 *	but we don't support that in request.
			 */
			if (vp->type == VT_XLAT) {
				vp->vp_strvalue = vp->value.xlat;
				vp->value.xlat = NULL;
				vp->type = VT_DATA;
			}

			if (!vp->da->vendor) switch (vp->da->attr) {
			default:
				break;

				/*
				 *	Allow it to set the packet type in
				 *	the attributes read from the file.
				 */
			case PW_PACKET_TYPE:
				request->packet->code = vp->vp_integer;
				break;

			case PW_PACKET_DST_PORT:
				request->packet->dst_port = (vp->vp_integer & 0xffff);
				break;

			case PW_PACKET_DST_IP_ADDRESS:
				request->packet->dst_ipaddr.af = AF_INET;
				request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
				break;

			case PW_PACKET_DST_IPV6_ADDRESS:
				request->packet->dst_ipaddr.af = AF_INET6;
				request->packet->dst_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
				break;

			case PW_PACKET_SRC_PORT:
				request->packet->src_port = (vp->vp_integer & 0xffff);
				break;

			case PW_PACKET_SRC_IP_ADDRESS:
				request->packet->src_ipaddr.af = AF_INET;
				request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
				break;

			case PW_PACKET_SRC_IPV6_ADDRESS:
				request->packet->src_ipaddr.af = AF_INET6;
				request->packet->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
				break;

			case PW_DIGEST_REALM:
			case PW_DIGEST_NONCE:
			case PW_DIGEST_METHOD:
			case PW_DIGEST_URI:
			case PW_DIGEST_QOP:
			case PW_DIGEST_ALGORITHM:
			case PW_DIGEST_BODY_DIGEST:
			case PW_DIGEST_CNONCE:
			case PW_DIGEST_NONCE_COUNT:
			case PW_DIGEST_USER_NAME:
				/* overlapping! */
				{
					DICT_ATTR const *da;
					uint8_t *p, *q;

					p = talloc_array(vp, uint8_t, vp->length + 2);

					memcpy(p + 2, vp->vp_octets, vp->length);
					p[0] = vp->da->attr - PW_DIGEST_REALM + 1;
					vp->length += 2;
					p[1] = vp->length;

					da = dict_attrbyvalue(PW_DIGEST_ATTRIBUTES, 0);
					if (!da) {
						ERROR("Out of memory");
						goto error;
					}
					vp->da = da;

					/*
					 *	Re-do pairmemsteal ourselves,
					 *	because we play games with
					 *	vp->da, and pairmemsteal goes
					 *	to GREAT lengths to sanitize
					 *	and fix and change and
					 *	double-check the various
					 *	fields.
					 */
					memcpy(&q, &vp->vp_octets, sizeof(q));
					talloc_free(q);

					vp->vp_octets = talloc_steal(vp, p);
					vp->type = VT_DATA;

					VERIFY_VP(vp);
				}

				break;
			}
		} /* loop over the VP's we read in */

		/*
		 *	Automatically set the port if we don't have a global
		 *	or packet specific one.
		 */
		if ((server_port == 0) && (request->packet->dst_port == 0)) {
			radclient_get_port(request->packet->code, &request->packet->dst_port);
		}

		/*
		 *	Add it to the tail of the list.
		 */
		if (!request_head) {
			assert(rc_request_tail == NULL);
			request_head = request;
			request->prev = NULL;
		} else {
			assert(rc_request_tail->next == NULL);
			rc_request_tail->next = request;
			request->prev = rc_request_tail;
		}
		rc_request_tail = request;
		request->next = NULL;

	} while (!packets_done); /* loop until the file is done. */

	if (packets != stdin) fclose(packets);
	if (filters) fclose(filters);

	/*
	 *	And we're done.
	 */
	return 1;

error:
	talloc_free(request);

	if (packets != stdin) fclose(packets);
	if (filters) fclose(filters);

	return 0;
}
示例#11
0
/** Copy packet to multiple servers
 *
 * Create a duplicate of the packet and send it to a list of realms
 * defined by the presence of the Replicate-To-Realm VP in the control
 * list of the current request.
 *
 * This is pretty hacky and is 100% fire and forget. If you're looking
 * to forward authentication requests to multiple realms and process
 * the responses, this function will not allow you to do that.
 *
 * @param[in] instance 	of this module.
 * @param[in] request 	The current request.
 * @param[in] list	of attributes to copy to the duplicate packet.
 * @param[in] code	to write into the code field of the duplicate packet.
 * @return RCODE fail on error, invalid if list does not exist, noop if no replications succeeded, else ok.
 */
static int replicate_packet(UNUSED void *instance, REQUEST *request, pair_lists_t list, unsigned int code)
{
	int rcode = RLM_MODULE_NOOP;
	VALUE_PAIR *vp, **vps;
	vp_cursor_t cursor;
	home_server_t *home;
	REALM *realm;
	home_pool_t *pool;
	RADIUS_PACKET *packet = NULL;

	/*
	 *	Send as many packets as necessary to different
	 *	destinations.
	 */
	fr_cursor_init(&cursor, &request->config);
	while ((vp = fr_cursor_next_by_num(&cursor, PW_REPLICATE_TO_REALM, 0, TAG_ANY))) {
		realm = realm_find2(vp->vp_strvalue);
		if (!realm) {
			REDEBUG2("Cannot Replicate to unknown realm \"%s\"", vp->vp_strvalue);
			continue;
		}

		/*
		 *	We shouldn't really do this on every loop.
		 */
		switch (request->packet->code) {
		default:
			REDEBUG2("Cannot replicate unknown packet code %d", request->packet->code);
			cleanup(packet);
			return RLM_MODULE_FAIL;

		case PW_CODE_ACCESS_REQUEST:
			pool = realm->auth_pool;
			break;

#ifdef WITH_ACCOUNTING

		case PW_CODE_ACCOUNTING_REQUEST:
			pool = realm->acct_pool;
			break;
#endif

#ifdef WITH_COA
		case PW_CODE_COA_REQUEST:
		case PW_CODE_DISCONNECT_REQUEST:
			pool = realm->coa_pool;
			break;
#endif
		}

		if (!pool) {
			RWDEBUG2("Cancelling replication to Realm %s, as the realm is local", realm->name);
			continue;
		}

		home = home_server_ldb(realm->name, pool, request);
		if (!home) {
			REDEBUG2("Failed to find live home server for realm %s", realm->name);
			continue;
		}

		/*
		 *	For replication to multiple servers we re-use the packet
		 *	we built here.
		 */
		if (!packet) {
			packet = rad_alloc(request, true);
			if (!packet) {
				return RLM_MODULE_FAIL;
			}

			packet->code = code;
			packet->id = fr_rand() & 0xff;
			packet->sockfd = fr_socket(&home->src_ipaddr, 0);
			if (packet->sockfd < 0) {
				REDEBUG("Failed opening socket: %s", fr_strerror());
				rcode = RLM_MODULE_FAIL;
				goto done;
			}

			vps = radius_list(request, list);
			if (!vps) {
				RWDEBUG("List '%s' doesn't exist for this packet",
					fr_int2str(pair_lists, list, "<INVALID>"));
				rcode = RLM_MODULE_INVALID;
				goto done;
			}

			/*
			 *	Don't assume the list actually contains any
			 *	attributes.
			 */
			if (*vps) {
				packet->vps = fr_pair_list_copy(packet, *vps);
				if (!packet->vps) {
					rcode = RLM_MODULE_FAIL;
					goto done;
				}
			}

			/*
			 *	For CHAP, create the CHAP-Challenge if
			 *	it doesn't exist.
			 */
			if ((code == PW_CODE_ACCESS_REQUEST) &&
			    (fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) != NULL) &&
			    (fr_pair_find_by_num(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL)) {
				uint8_t *p;
				vp = radius_pair_create(packet, &packet->vps, PW_CHAP_CHALLENGE, 0);
				vp->length = AUTH_VECTOR_LEN;
				vp->vp_octets = p = talloc_array(vp, uint8_t, vp->length);
				memcpy(p, request->packet->vector, AUTH_VECTOR_LEN);
			}
		} else {
			size_t i;

			for (i = 0; i < sizeof(packet->vector); i++) {
				packet->vector[i] = fr_rand() & 0xff;
			}

			packet->id++;
			TALLOC_FREE(packet->data);
			packet->data_len = 0;
		}

		/*
		 *	(Re)-Write these.
		 */
		packet->dst_ipaddr = home->ipaddr;
		packet->dst_port = home->port;
		memset(&packet->src_ipaddr, 0, sizeof(packet->src_ipaddr));
		packet->src_port = 0;

		/*
		 *	Encode, sign and then send the packet.
		 */
		RDEBUG("Replicating list '%s' to Realm '%s'", fr_int2str(pair_lists, list, "<INVALID>"),
		       realm->name);
		if (rad_send(packet, NULL, home->secret) < 0) {
			REDEBUG("Failed replicating packet: %s", fr_strerror());
			rcode = RLM_MODULE_FAIL;
			goto done;
		}

		/*
		 *	We've sent it to at least one destination.
		 */
		rcode = RLM_MODULE_OK;
	}

	done:

	cleanup(packet);
	return rcode;
}