예제 #1
0
static void perl_vp_to_svpvn_element(REQUEST *request, AV *av, VALUE_PAIR const *vp,
				     int *i, const char *hash_name, const char *list_name)
{
	size_t len;

	char buffer[1024];

	switch (vp->da->type) {
	case PW_TYPE_STRING:
		RDEBUG("$%s{'%s'}[%i] = &%s:%s -> '%s'", hash_name, vp->da->name, *i,
		       list_name, vp->da->name, vp->vp_strvalue);
		av_push(av, newSVpvn(vp->vp_strvalue, vp->vp_length));
		break;

	case PW_TYPE_OCTETS:
		if (RDEBUG_ENABLED) {
			char *hex;

			hex = fr_abin2hex(request, vp->vp_octets, vp->vp_length);
			RDEBUG("$%s{'%s'}[%i] = &%s:%s -> 0x%s", hash_name, vp->da->name, *i,
			       list_name, vp->da->name, hex);
			talloc_free(hex);
		}
		av_push(av, newSVpvn((char const *)vp->vp_octets, vp->vp_length));
		break;

	default:
		len = fr_pair_value_snprint(buffer, sizeof(buffer), vp, 0);
		RDEBUG("$%s{'%s'}[%i] = &%s:%s -> '%s'", hash_name, vp->da->name, *i,
		       list_name, vp->da->name, buffer);
		av_push(av, newSVpvn(buffer, truncate_len(len, sizeof(buffer))));
		break;
	}
	(*i)++;
}
예제 #2
0
static void perl_vp_to_svpvn_element(REQUEST *request, AV *av, VALUE_PAIR const *vp,
				     int *i, const char *hash_name, const char *list_name)
{

	SV *sv;

	switch (vp->vp_type) {
	case FR_TYPE_STRING:
		RDEBUG2("$%s{'%s'}[%i] = &%s:%s -> '%s'", hash_name, vp->da->name, *i,
		        list_name, vp->da->name, vp->vp_strvalue);
		sv = newSVpvn(vp->vp_strvalue, vp->vp_length);
		break;

	case FR_TYPE_OCTETS:
		RDEBUG2("$%s{'%s'}[%i] = &%s:%s -> 0x%pH", hash_name, vp->da->name, *i,
		        list_name, vp->da->name, &vp->data);
		sv = newSVpvn((char const *)vp->vp_octets, vp->vp_length);
		break;

	default:
	{
		char	buffer[1024];
		size_t	len;

		len = fr_pair_value_snprint(buffer, sizeof(buffer), vp, '\0');
		RDEBUG2("$%s{'%s'}[%i] = &%s:%s -> '%s'", hash_name, vp->da->name, *i,
		        list_name, vp->da->name, buffer);
		sv = newSVpvn(buffer, truncate_len(len, sizeof(buffer)));
	}
		break;
	}

	if (!sv) return;
	SvTAINT(sv);
	av_push(av, sv);
	(*i)++;
}
예제 #3
0
/** Write the result of a set operation back to net-snmp
 *
 * Writes "DONE\n" on success, or an error as described in man snmpd.conf
 * on error.
 *
 * @param fd to write to.
 * @param error attribute.
 * @param head of list of attributes to convert and write.
 * @return
 *	- 0 on success.
 *	- -1 on failure.
 */
static int radsnmp_set_response(int fd, fr_dict_attr_t const *error, VALUE_PAIR *head)
{
	VALUE_PAIR	*vp;
	char		buffer[64];
	size_t		len;
	struct iovec	io_vector[2];
	char		newline[] = "\n";

	vp = fr_pair_find_by_da(head, error, TAG_NONE);
	if (!vp) {
		if (write(fd, "DONE\n", 5) < 0) {
			fr_strerror_printf("Failed writing set response: %s", fr_syserror(errno));
			return -1;
		}
		return 0;
	}

	len = fr_pair_value_snprint(buffer, sizeof(buffer), vp, '\0');
	if (is_truncated(len, sizeof(buffer))) {
		assert(0);
		return -1;
	}

	io_vector[0].iov_base = buffer;
	io_vector[0].iov_len = len;
	io_vector[1].iov_base = newline;
	io_vector[1].iov_len = 1;

	DEBUG2("said: %s", buffer);

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

	return 0;
}
예제 #4
0
/*
 *  	get the vps and put them in perl hash
 *  	If one VP have multiple values it is added as array_ref
 *  	Example for this is Cisco-AVPair that holds multiple values.
 *  	Which will be available as array_ref in $RAD_REQUEST{'Cisco-AVPair'}
 */
static void perl_store_vps(UNUSED TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR **vps, HV *rad_hv,
			   const char *hash_name, const char *list_name)
{
	VALUE_PAIR *vp;

	hv_undef(rad_hv);

	fr_cursor_t cursor;

	RINDENT();
	fr_pair_list_sort(vps, fr_pair_cmp_by_da_tag);
	for (vp = fr_cursor_init(&cursor, vps);
	     vp;
	     vp = fr_cursor_next(&cursor)) {
		VALUE_PAIR *next;

		char const *name;
		char namebuf[256];

		/*
		 *	Tagged attributes are added to the hash with name
		 *	<attribute>:<tag>, others just use the normal attribute
		 *	name as the key.
		 */
		if (vp->da->flags.has_tag && (vp->tag != TAG_ANY)) {
			snprintf(namebuf, sizeof(namebuf), "%s:%d", vp->da->name, vp->tag);
			name = namebuf;
		} else {
			name = vp->da->name;
		}

		/*
		 *	We've sorted by type, then tag, so attributes of the
		 *	same type/tag should follow on from each other.
		 */
		if ((next = fr_cursor_next_peek(&cursor)) && ATTRIBUTE_EQ(vp, next)) {
			int i = 0;
			AV *av;

			av = newAV();
			perl_vp_to_svpvn_element(request, av, vp, &i, hash_name, list_name);
			do {
				perl_vp_to_svpvn_element(request, av, next, &i, hash_name, list_name);
				fr_cursor_next(&cursor);
			} while ((next = fr_cursor_next_peek(&cursor)) && ATTRIBUTE_EQ(vp, next));
			(void)hv_store(rad_hv, name, strlen(name), newRV_noinc((SV *)av), 0);

			continue;
		}

		/*
		 *	It's a normal single valued attribute
		 */
		switch (vp->vp_type) {
		case FR_TYPE_STRING:
			RDEBUG2("$%s{'%s'} = &%s:%s -> '%pV'", hash_name, vp->da->name, list_name,
			       vp->da->name, &vp->data);
			(void)hv_store(rad_hv, name, strlen(name), newSVpvn(vp->vp_strvalue, vp->vp_length), 0);
			break;

		case FR_TYPE_OCTETS:
			RDEBUG2("$%s{'%s'} = &%s:%s -> %pV", hash_name, vp->da->name, list_name,
			       vp->da->name, &vp->data);
			(void)hv_store(rad_hv, name, strlen(name),
				       newSVpvn((char const *)vp->vp_octets, vp->vp_length), 0);
			break;

		default:
		{
			char buffer[1024];
			size_t len;

			len = fr_pair_value_snprint(buffer, sizeof(buffer), vp, '\0');
			RDEBUG2("$%s{'%s'} = &%s:%s -> '%s'", hash_name, vp->da->name,
			       list_name, vp->da->name, buffer);
			(void)hv_store(rad_hv, name, strlen(name),
				       newSVpvn(buffer, truncate_len(len, sizeof(buffer))), 0);
		}
			break;
		}
	}
	REXDENT();
}
예제 #5
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
}
예제 #6
0
/** Write the result of a get or getnext operation back to net-snmp
 *
 * Returns three lines of output per attribute:
 * - OID string
 * - type
 * - value
 *
 * Index attributes (num 0) must be in order of depth (shallowest first).
 *
 * If no attributes were written, will write "NONE\n" to inform net-snmp
 * that no value was available at the specified OID.
 *
 * @param fd to write to.
 * @param root of the SNMP portion of the main dictionary.
 * @param type attribute.
 * @param head of list of attributes to convert and write.
 * @return
 *	- >=0 on success (the number of varbind responses written).
 *	- -1 on failure.
 */
static int radsnmp_get_response(int fd,
				fr_dict_attr_t const *root, fr_dict_attr_t const *type,
				VALUE_PAIR *head)
{
	fr_cursor_t		cursor;
	VALUE_PAIR		*vp, *type_vp;
	fr_dict_attr_t const	*parent = root;
	unsigned int		written = 0;

	ssize_t			slen;
	size_t			len;

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

	struct iovec		io_vector[6];

	char			newline[] = "\n";

	type_buff[0] = '\0';

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

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

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

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

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

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

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

			p += len;

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

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

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

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

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

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

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

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

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

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

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

	return written;
}
예제 #7
0
/** Add session controls to a connection as per draft-wahl-ldap-session
 *
 * @note the RFC states that the username identifier, must be the authenticated
 *	user id, not the purported one. As order of operations is configurable,
 *	we're going to leave that up to the server admin to satisfy that
 *	requirement
 *
 * For once the RFC is pretty helpful about what should be inserted into the
 * various values, and maps out RADIUS attributes to formatOIDs, so none of
 * this is configurable.
 *
 * @param conn to add controls to.
 * @param request to draw attributes from.
 */
int fr_ldap_control_add_session_tracking(fr_ldap_connection_t *conn, REQUEST *request)
{
	/*
	 *	The OpenLDAP guys didn't declare the formatOID parameter to
	 *	ldap_create_session_tracking_control as const *sigh*.
	 */
	static char 		username_oid[] = LDAP_CONTROL_X_SESSION_TRACKING_USERNAME;
	static char 		acctsessionid_oid[] = LDAP_CONTROL_X_SESSION_TRACKING_RADIUS_ACCT_SESSION_ID;
	static char 		acctmultisessionid_oid[] = LDAP_CONTROL_X_SESSION_TRACKING_RADIUS_ACCT_MULTI_SESSION_ID;

	int			ret;

	char			ipaddress[INET6_ADDRSTRLEN];
	char			*username = NULL;
	char			*acctsessionid = NULL;
	char			*acctmultisessionid = NULL;
	char			*hostname;

	LDAPControl		*username_control = NULL;
	LDAPControl		*acctsessionid_control = NULL;
	LDAPControl		*acctmultisessionid_control = NULL;
	struct berval		tracking_id;

	fr_cursor_t		cursor;
	VALUE_PAIR const	*vp;

	memcpy(&hostname, main_config->name, sizeof(hostname)); /* const / non-const issues */

	for (vp = fr_cursor_init(&cursor, &request->packet->vps);
	     vp;
	     vp = fr_cursor_next(&cursor)) {
		if (fr_dict_attr_is_top_level(vp->da)) switch (vp->da->attr) {
		case FR_NAS_IP_ADDRESS:
		case FR_NAS_IPV6_ADDRESS:
			fr_pair_value_snprint(ipaddress, sizeof(ipaddress), vp, '\0');
			break;

		case FR_USER_NAME:
			memcpy(&username, &vp->vp_strvalue, sizeof(username));
			break;

		case FR_ACCT_SESSION_ID:
			memcpy(&acctsessionid, &vp->vp_strvalue, sizeof(acctsessionid));
			break;

		case FR_ACCT_MULTI_SESSION_ID:
			memcpy(&acctmultisessionid, &vp->vp_strvalue, sizeof(acctmultisessionid));
			break;
		}
	}

	if (username) {
		tracking_id.bv_val = username;
		tracking_id.bv_len = talloc_array_length(username) - 1;

		ret = ldap_create_session_tracking_control(conn->handle, ipaddress,
							   hostname,
							   username_oid,
							   &tracking_id,
							   &username_control);
		if (ret != LDAP_SUCCESS) {
			REDEBUG("Failed creating username session tracking control: %s", ldap_err2string(ret));
		error:
			if (username_control) ldap_control_free(username_control);
			if (acctsessionid_control) ldap_control_free(acctsessionid_control);
			if (acctmultisessionid_control) ldap_control_free(acctmultisessionid_control);
			return -1;
		}
	}

	if (acctsessionid) {
		tracking_id.bv_val = acctsessionid;
		tracking_id.bv_len = talloc_array_length(acctsessionid) - 1;

		ret = ldap_create_session_tracking_control(conn->handle, ipaddress,
							   hostname,
							   acctsessionid_oid,
							   &tracking_id,
							   &acctsessionid_control);
		if (ret != LDAP_SUCCESS) {
			REDEBUG("Failed creating acctsessionid session tracking control: %s", ldap_err2string(ret));
			goto error;
		}
	}

	if (acctmultisessionid) {
		tracking_id.bv_val = acctmultisessionid;
		tracking_id.bv_len = talloc_array_length(acctmultisessionid) - 1;

		ret = ldap_create_session_tracking_control(conn->handle, ipaddress,
							   hostname,
							   acctmultisessionid_oid,
							   &tracking_id,
							   &acctmultisessionid_control);
		if (ret != LDAP_SUCCESS) {
			REDEBUG("Failed creating acctmultisessionid session tracking control: %s",
				ldap_err2string(ret));
			goto error;
		}
	}

	if ((conn->serverctrls_cnt + 3) >= LDAP_MAX_CONTROLS) {
		REDEBUG("Insufficient space to add session tracking controls");
		goto error;
	}

	if (username_control && (fr_ldap_control_add_server(conn, username_control, true) < 0)) goto error;

	if (acctsessionid_control && (fr_ldap_control_add_server(conn, acctsessionid_control, true) < 0)) {
		conn->serverctrls_cnt--;
		conn->serverctrls[conn->serverctrls_cnt].control = NULL;
		goto error;
	}

	if (acctmultisessionid_control && (fr_ldap_control_add_server(conn, acctmultisessionid_control, true) < 0)) {
		conn->serverctrls_cnt--;
		conn->serverctrls[conn->serverctrls_cnt].control = NULL;
		conn->serverctrls_cnt--;
		conn->serverctrls[conn->serverctrls_cnt].control = NULL;
		goto error;
	}

	return 0;
}
예제 #8
0
/** Unpack data
 *
 *  Example: %{unpack:&Class 0 integer}
 *
 *  Expands Class, treating octet at offset 0 (bytes 0-3) as an "integer".
 */
static ssize_t unpack_xlat(UNUSED void *instance, REQUEST *request, char const *fmt,
                           char **out, size_t outlen)
{
    char *data_name, *data_size, *data_type;
    char *p;
    size_t len, input_len;
    int offset;
    PW_TYPE type;
    DICT_ATTR const *da;
    VALUE_PAIR *vp, *cast;
    uint8_t const *input;
    char buffer[256];
    uint8_t blob[256];

    /*
     *	FIXME: copy only the fields here, as we parse them.
     */
    strlcpy(buffer, fmt, sizeof(buffer));

    p = buffer;
    while (isspace((int) *p)) p++; /* skip leading spaces */

    data_name = p;

    while (*p && !isspace((int) *p)) p++;

    if (!*p) {
error:
        REDEBUG("Format string should be '<data> <offset> <type>' e.g. '&Class 1 integer'");
nothing:
        return -1;
    }

    while (isspace((int) *p)) *(p++) = '\0';
    if (!*p) GOTO_ERROR;

    data_size = p;

    while (*p && !isspace((int) *p)) p++;
    if (!*p) GOTO_ERROR;

    while (isspace((int) *p)) *(p++) = '\0';
    if (!*p) GOTO_ERROR;

    data_type = p;

    while (*p && !isspace((int) *p)) p++;
    if (*p) GOTO_ERROR;	/* anything after the type is an error */

    /*
     *	Attribute reference
     */
    if (*data_name == '&') {
        if (radius_get_vp(&vp, request, data_name) < 0) goto nothing;

        if ((vp->da->type != PW_TYPE_OCTETS) &&
                (vp->da->type != PW_TYPE_STRING)) {
            REDEBUG("unpack requires the input attribute to be 'string' or 'octets'");
            goto nothing;
        }
        input = vp->vp_octets;
        input_len = vp->vp_length;

    } else if ((data_name[0] == '0') && (data_name[1] == 'x')) {
        /*
         *	Hex data.
         */
        len = strlen(data_name + 2);
        if ((len & 0x01) != 0) {
            RDEBUG("Invalid hex string in '%s'", data_name);
            goto nothing;
        }
        input = blob;
        input_len = fr_hex2bin(blob, sizeof(blob), data_name + 2, len);

    } else {
        GOTO_ERROR;
    }

    offset = (int) strtoul(data_size, &p, 10);
    if (*p) {
        REDEBUG("unpack requires a decimal number, not '%s'", data_size);
        goto nothing;
    }

    type = fr_str2int(dict_attr_types, data_type, PW_TYPE_INVALID);
    if (type == PW_TYPE_INVALID) {
        REDEBUG("Invalid data type '%s'", data_type);
        goto nothing;
    }

    /*
     *	Output must be a non-zero limited size.
     */
    if ((dict_attr_sizes[type][0] ==  0) ||
            (dict_attr_sizes[type][0] != dict_attr_sizes[type][1])) {
        REDEBUG("unpack requires fixed-size output type, not '%s'", data_type);
        goto nothing;
    }

    if (input_len < (offset + dict_attr_sizes[type][0])) {
        REDEBUG("Insufficient data to unpack '%s' from '%s'", data_type, data_name);
        goto nothing;
    }

    da = dict_attrbyvalue(PW_CAST_BASE + type, 0);
    if (!da) {
        REDEBUG("Cannot decode type '%s'", data_type);
        goto nothing;
    }

    cast = fr_pair_afrom_da(request, da);
    if (!cast) goto nothing;

    memcpy(&(cast->data), input + offset, dict_attr_sizes[type][0]);
    cast->vp_length = dict_attr_sizes[type][0];

    /*
     *	Hacks
     */
    switch (type) {
    case PW_TYPE_SIGNED:
    case PW_TYPE_INTEGER:
    case PW_TYPE_DATE:
        cast->vp_integer = ntohl(cast->vp_integer);
        break;

    case PW_TYPE_SHORT:
        cast->vp_short = ((input[offset] << 8) | input[offset + 1]);
        break;

    case PW_TYPE_INTEGER64:
        cast->vp_integer64 = ntohll(cast->vp_integer64);
        break;

    default:
        break;
    }

    len = fr_pair_value_snprint(*out, outlen, cast, 0);
    talloc_free(cast);
    if (is_truncated(len, outlen)) {
        REDEBUG("Insufficient buffer space to unpack data");
        goto nothing;
    }

    return len;
}