Beispiel #1
0
/** Return a VP from a value_pair_tmpl_t
 *
 * @param out where to write the retrieved vp.
 * @param request current request.
 * @param vpt the value pair template
 * @return -1 if VP could not be found, -2 if list could not be found, -3 if context could not be found.
 */
int tmpl_find_vp(VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt)
{
	VALUE_PAIR **vps, *vp;

	rad_assert((vpt->type == TMPL_TYPE_ATTR) || (vpt->type == TMPL_TYPE_LIST));

	if (out) *out = NULL;

	if (radius_request(&request, vpt->tmpl_request) < 0) {
		return -3;
	}

	vps = radius_list(request, vpt->tmpl_list);
	if (!vps) {
		return -2;
	}

	switch (vpt->type) {
	/*
	 *	May not may not be found, but it *is* a known name.
	 */
	case TMPL_TYPE_ATTR:
	{
		int num;
		vp_cursor_t cursor;

		if (vpt->tmpl_num == NUM_ANY) {
			vp = pairfind(*vps, vpt->tmpl_da->attr, vpt->tmpl_da->vendor, vpt->tmpl_tag);
			if (!vp) return -1;
			break;
		}

		(void) fr_cursor_init(&cursor, vps);
		num = vpt->tmpl_num;
		while ((vp = fr_cursor_next_by_da(&cursor, vpt->tmpl_da, vpt->tmpl_tag))) {
			VERIFY_VP(vp);
			if (num-- <= 0) goto finish;
		}
		return -1;
	}

	case TMPL_TYPE_LIST:
		vp = *vps;
		break;

	default:
		rad_assert(0);
	}

finish:
	if (out) *out = vp;

	return 0;
}
/** Allocate a request packet
 *
 * This is done once per request with the same packet being sent to multiple realms.
 */
static rlm_rcode_t rlm_replicate_alloc(RADIUS_PACKET **out, REQUEST *request, pair_lists_t list, PW_CODE code)
{
	rlm_rcode_t rcode = RLM_MODULE_OK;
	RADIUS_PACKET *packet = NULL;
	VALUE_PAIR *vp, **vps;

	*out = NULL;

	packet = rad_alloc(request, 1);
	if (!packet) {
		return RLM_MODULE_FAIL;
	}
	packet->code = code;

	/*
	 *	Figure out which list in the request were replicating
	 */
	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 error;
	}

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

	/*
	 *	For CHAP, create the CHAP-Challenge if it doesn't exist.
	 */
	if ((code == PW_CODE_ACCESS_REQUEST) &&
	    (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) != NULL) &&
	    (pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL)) {
		vp = radius_paircreate(packet, &packet->vps, PW_CHAP_CHALLENGE, 0);
		pairmemcpy(vp, request->packet->vector, AUTH_VECTOR_LEN);
	}

	*out = packet;
	return rcode;

error:
	talloc_free(packet);
	return rcode;
}
Beispiel #3
0
/** Convert a valuepair string to VALUE_PAIR and insert it into a list
 *
 * Takes a valuepair string with list and request qualifiers, converts it into a VALUE_PAIR
 * and inserts it into the appropriate list.
 *
 * @param request Current request.
 * @param raw string to parse.
 * @param request_def to use if attribute isn't qualified.
 * @param list_def to use if attribute isn't qualified.
 * @return 0 on success, -1 on error.
 */
int radius_str2vp(REQUEST *request, char const *raw, request_refs_t request_def, pair_lists_t list_def)
{
	char const *p;
	size_t len;
	request_refs_t req;
	pair_lists_t list;

	VALUE_PAIR *vp = NULL;
	VALUE_PAIR **vps;

	p = raw;

	req = radius_request_name(&p, request_def);
	len = p - raw;
	if (req == REQUEST_UNKNOWN) {
		REDEBUG("Invalid request qualifier \"%.*s\"", (int) len, raw);

		return -1;
	}
	raw += len;

	list = radius_list_name(&p, list_def);
	if (list == PAIR_LIST_UNKNOWN) {
		len = p - raw;

		REDEBUG("Invalid list qualifier \"%.*s\"", (int) len, raw);

		return -1;
	}
	raw += len;

	if (radius_request(&request, req) < 0) {
		return -1;
	}

	vps = radius_list(request, list);
	if (!vps) {
		return -1;
	}

	if (userparse(request, raw, &vp) == T_OP_INVALID) {
		return -1;
	}

	pairmove(request, vps, &vp);

	return 0;
}
Beispiel #4
0
/** Convert VALUE_PAIR_MAP to VALUE_PAIR(s) and add them to a REQUEST.
 *
 * Takes a single VALUE_PAIR_MAP, resolves request and list identifiers
 * to pointers in the current request, the attempts to retrieve module
 * specific value(s) using callback, and adds the resulting values to the
 * correct request/list.
 *
 * @param request The current request.
 * @param map specifying destination attribute and location and src identifier.
 * @param func to retrieve module specific values and convert them to
 *	VLAUE_PAIRS.
 * @param ctx to be passed to func.
 * @param src name to be used in debugging if different from map value.
 * @return -1 if either attribute or qualifier weren't valid in this context
 *	or callback returned NULL pointer, else 0.
 */
int radius_map2request(REQUEST *request, const VALUE_PAIR_MAP *map,
		       const char *src, radius_tmpl_getvalue_t func, void *ctx)
{
	VALUE_PAIR **list, *vp, *head;
	char buffer[MAX_STRING_LEN];
	
	if (radius_request(&request, map->dst->request) < 0) {
		RDEBUG("WARNING: Request in mapping \"%s\" -> \"%s\" "
		       "invalid in this context, skipping!",
		       map->src->name, map->dst->name);
		
		return -1;
	}
	
	list = radius_list(request, map->dst->list);
	if (!list) {
		RDEBUG("WARNING: List in mapping \"%s\" -> \"%s\" "
		       "invalid in this context, skipping!",
		       map->src->name, map->dst->name);
		       
		return -1;
	}
	
	head = func(request, map->dst, ctx);
	if (head == NULL) {
		return -1;
	}
	
	for (vp = head; vp != NULL; vp = vp->next) {
		vp->operator = map->op_token;
		
		if (debug_flag) {
			vp_prints_value(buffer, sizeof(buffer), vp, 1);
			
			RDEBUG("\t%s %s %s (%s)", map->dst->name,
			       fr_int2str(fr_tokens, vp->operator, "¿unknown?"), 
			       buffer, src ? src : map->src->name);
		}
	}
	
	/*
	 *	Use pairmove so the operator is respected
	 */
	radius_pairmove(request, list, head);
	pairfree(&vp); /* Free the VP if for some reason it wasn't moved */
	
	return 0;
}
Beispiel #5
0
/** Copy pairs matching a VPT in the current request
 *
 * @param out where to write the copied vps.
 * @param request current request.
 * @param vpt the value pair template
 * @return -1 if VP could not be found, -2 if list could not be found, -3 if context could not be found.
 */
int radius_vpt_copy_vp(VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt)
{
	VALUE_PAIR **vps, *vp;
	REQUEST *current = request;

	if (out) *out = NULL;

	if (radius_request(&current, vpt->request) < 0) {
		return -3;
	}

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

	switch (vpt->type) {
	/*
	 *	May not may not be found, but it *is* a known name.
	 */
	case VPT_TYPE_ATTR:
		vp = paircopy2(request, *vps, vpt->da->attr, vpt->da->vendor, TAG_ANY);
		if (!vp) {
			return -1;
		}
		break;

	case VPT_TYPE_LIST:
		vp = paircopy(request, *vps);

		break;

	default:
		/*
		 *	literal, xlat, regex, exec, data.
		 *	no attribute.
		 */
		return -1;
	}

	if (out) {
		*out = vp;
	}

	return 0;
}
/** Return a VP from the specified request.
 *
 * @param request current request.
 * @param name attribute name including qualifiers.
 * @param vp_p where to write the pointer to the resolved VP. 
 *	Will be NULL if the attribute couldn't be resolved.
 * @return -1 if either the attribute or qualifier were invalid, else 0
 */
int radius_get_vp(REQUEST *request, const char *name, VALUE_PAIR **vp_p)
{
	value_pair_tmpl_t vpt;
	VALUE_PAIR **vps;

	*vp_p = NULL;
	
	if (radius_parse_attr(name, &vpt, REQUEST_CURRENT,
	    PAIR_LIST_REQUEST) < 0) {
		return -1;
	}
	
	if (radius_request(&request, vpt.request) < 0) {
		return 0;
	}
	
	vps = radius_list(request, vpt.list);
	if (!vps) {    
		return 0;
	}
	
	switch (vpt.type)
	{
	/*
	 *	May not may not be found, but it *is* a known name.
	 */
	case VPT_TYPE_ATTR:
		*vp_p = pairfind(*vps, vpt.da->attr, vpt.da->vendor, TAG_ANY);
		break;
		
	case VPT_TYPE_LIST:
		*vp_p = *vps;
		break;
		
	default:
		rad_assert(0);
		return -1;
		break;
	}

	return 0;
}
Beispiel #7
0
/*
 *	Do xlat of strings.
 */
static ssize_t exec_xlat(void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen)
{
	int		result;
	rlm_exec_t	*inst = instance;
	VALUE_PAIR	**input_pairs = NULL;
	char *p;

	if (!inst->wait) {
		REDEBUG("'wait' must be enabled to use exec xlat");
		*out = '\0';
		return -1;
	}

	if (inst->input_list) {
		input_pairs = radius_list(request, inst->input_list);
		if (!input_pairs) {
			REDEBUG("Failed to find input pairs for xlat");
			*out = '\0';
			return -1;
		}
	}

	/*
	 *	This function does it's own xlat of the input program
	 *	to execute.
	 */
	result = radius_exec_program(request, fmt, inst->wait, inst->shell_escape,
				     out, outlen, inst->timeout,
				     input_pairs ? *input_pairs : NULL, NULL);
	if (result != 0) {
		out[0] = '\0';
		return -1;
	}

	for (p = out; *p != '\0'; p++) {
		if (*p < ' ') *p = ' ';
	}

	return strlen(out);
}
Beispiel #8
0
/** Return a VP from a value_pair_tmpl_t
 *
 * @param out where to write the retrieved vp.
 * @param request current request.
 * @param vpt the value pair template
 * @return -1 if VP could not be found, -2 if list could not be found, -3 if context could not be found.
 */
int radius_vpt_get_vp(VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt)
{
	VALUE_PAIR **vps, *vp;

	if (out) *out = NULL;

	if (radius_request(&request, vpt->request) < 0) {
		return -3;
	}

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

	switch (vpt->type) {
	/*
	 *	May not may not be found, but it *is* a known name.
	 */
	case VPT_TYPE_ATTR:
		vp = pairfind(*vps, vpt->da->attr, vpt->da->vendor, TAG_ANY);
		if (!vp) {
			return -1;
		}
		break;

	case VPT_TYPE_LIST:
		vp = *vps;
	default:
		break;
	}

	if (out) {
		*out = vp;
	}

	return 0;
}
Beispiel #9
0
/** Return a VP from the specified request.
 *
 * @param request current request.
 * @param name attribute name including qualifiers.
 * @param vp_p where to write the pointer to the resolved VP. 
 *	Will be NULL if the attribute couldn't be resolved.
 * @return False if either the attribute or qualifier were invalid, else true
 */
int radius_get_vp(REQUEST *request, const char *name, VALUE_PAIR **vp_p)
{
	VALUE_PAIR **vps;
	pair_lists_t list;
	
	const DICT_ATTR *da;
	
	*vp_p = NULL;
	
	if (!radius_ref_request(&request, &name)) {
		RDEBUG("WARNING: Attribute name refers to outer request"
		       " but not in a tunnel.");
		return TRUE;	/* Discuss, we don't actually know if
				   the attrname was valid... */
	}
	
	list = radius_list_name(&name, PAIR_LIST_REQUEST);
	if (list == PAIR_LIST_UNKNOWN) {
		RDEBUG("ERROR: Invalid list qualifier");
		return FALSE;
	}
	
	da = dict_attrbyname(name);
	if (!da) {
		RDEBUG("ERROR: Attribute \"%s\" unknown", name);
		return FALSE;
	}

	vps = radius_list(request, list);
	rad_assert(vps);
	
	/*
	 *	May not may not be found, but it *is* a known name.
	 */
	*vp_p = pairfind(*vps, da->attr, da->vendor);
	return TRUE;
}
Beispiel #10
0
/** Return a VP from the specified request.
 *
 * @param request current request.
 * @param name attribute name including qualifiers.
 * @param vp_p where to write the pointer to the resolved VP. 
 *	Will be NULL if the attribute couldn't be resolved.
 * @return -1 if either the attribute or qualifier were invalid, else 0
 */
int radius_get_vp(REQUEST *request, const char *name, VALUE_PAIR **vp_p)
{
	VALUE_PAIR_TMPL vpt;
	VALUE_PAIR **vps;

	*vp_p = NULL;
	
	if (radius_parse_attr(name, &vpt, REQUEST_CURRENT,
	    PAIR_LIST_REQUEST) < 0) {
		return -1;
	}
	
	if (radius_request(&request, vpt.request) < 0) {
		RDEBUG("WARNING: Specified request \"%s\" is not available in "
		       "this context", fr_int2str(request_refs, vpt.request,
		       				  "¿unknown?"));
		       
		return 0;
	}
	
	vps = radius_list(request, vpt.list);
	if (!vps) {
		RDEBUG("WARNING: Specified list \"%s\" is not available in "
		       "this context", fr_int2str(pair_lists, vpt.list,
		       				  "¿unknown?"));
	       	       
		return 0;
	}
	
	/*
	 *	May not may not be found, but it *is* a known name.
	 */
	*vp_p = pairfind(*vps, vpt.da->attr, vpt.da->vendor, TAG_ANY);
	
	return 0;
}
Beispiel #11
0
/** Expand values in an attribute map where needed
 *
 */
int rlm_ldap_map_xlat(REQUEST *request, value_pair_map_t const *maps, rlm_ldap_map_xlat_t *expanded)
{
	const value_pair_map_t *map;
	unsigned int total = 0;
	size_t len;
	
	VALUE_PAIR *found, **from = NULL;
	REQUEST *context;

	for (map = maps; map != NULL; map = map->next) {
		switch (map->src->type) {
		case VPT_TYPE_XLAT:
			{
				char *exp = NULL;
			
				len = radius_xlat(exp, 0, request, map->src->name, NULL, NULL);		  
				if (len <= 0) {
					RDEBUG("Expansion of LDAP attribute \"%s\" failed", map->src->name);
				       
					goto error;
				}
			
				expanded->attrs[total++] = exp;
				break;
			}

		case VPT_TYPE_ATTR:
			context = request;
			
			if (radius_request(&context, map->src->request) == 0) {
				from = radius_list(context, map->src->list);
			}
			if (!from) continue;
			
			found = pairfind(*from, map->src->da->attr, map->src->da->vendor, TAG_ANY);
			if (!found) continue;
			
			expanded->attrs[total++] = talloc_strdup(request, found->vp_strvalue);
			break;
			
		case VPT_TYPE_LITERAL:
			expanded->attrs[total++] = map->src->name;
			break;
		default:
			rad_assert(0);
		error:
			expanded->attrs[total] = NULL;
			
			rlm_ldap_map_xlat_free(expanded);
			
			return -1;
		}
			
	}
	
	rad_assert(total < LDAP_MAX_ATTRMAP);
	
	expanded->attrs[total] = NULL;
	expanded->count = total;
	expanded->maps = maps;
	
	return 0;
}
Beispiel #12
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
}
Beispiel #13
0
/** Convert group membership information into attributes
 *
 * @param[in] inst rlm_ldap configuration.
 * @param[in] request Current request.
 * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect.
 * @param[in] entry retrieved by rlm_ldap_find_user or rlm_ldap_search.
 * @param[in] attr membership attribute to look for in the entry.
 * @return One of the RLM_MODULE_* values.
 */
rlm_rcode_t rlm_ldap_cacheable_userobj(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn,
				       LDAPMessage *entry, char const *attr)
{
	rlm_rcode_t rcode = RLM_MODULE_OK;

	struct berval **values;

	char *group_name[LDAP_MAX_CACHEABLE + 1];
	char **name_p = group_name;

	char *group_dn[LDAP_MAX_CACHEABLE + 1];
	char **dn_p;

	char *name;

	VALUE_PAIR *vp, **list, *groups = NULL;
	TALLOC_CTX *list_ctx, *value_ctx;
	vp_cursor_t list_cursor, groups_cursor;

	int is_dn, i, count;

	rad_assert(entry);
	rad_assert(attr);

	/*
	 *	Parse the membership information we got in the initial user query.
	 */
	values = ldap_get_values_len((*pconn)->handle, entry, attr);
	if (!values) {
		RDEBUG2("No cacheable group memberships found in user object");

		return RLM_MODULE_OK;
	}
	count = ldap_count_values_len(values);

	list = radius_list(request, PAIR_LIST_CONTROL);
	list_ctx = radius_list_ctx(request, PAIR_LIST_CONTROL);

	/*
	 *	Simplifies freeing temporary values
	 */
	value_ctx = talloc_new(request);

	/*
	 *	Temporary list to hold new group VPs, will be merged
	 *	once all group info has been gathered/resolved
	 *	successfully.
	 */
	fr_cursor_init(&groups_cursor, &groups);

	for (i = 0; (i < LDAP_MAX_CACHEABLE) && (i < count); i++) {
		is_dn = rlm_ldap_is_dn(values[i]->bv_val, values[i]->bv_len);

		if (inst->cacheable_group_dn) {
			/*
			 *	The easy case, we're caching DNs and we got a DN.
			 */
			if (is_dn) {
				MEM(vp = fr_pair_afrom_da(list_ctx, inst->cache_da));
				fr_pair_value_bstrncpy(vp, values[i]->bv_val, values[i]->bv_len);
				fr_cursor_insert(&groups_cursor, vp);
			/*
			 *	We were told to cache DNs but we got a name, we now need to resolve
			 *	this to a DN. Store all the group names in an array so we can do one query.
			 */
			} else {
				*name_p++ = rlm_ldap_berval_to_string(value_ctx, values[i]);
			}
		}

		if (inst->cacheable_group_name) {
			/*
			 *	The easy case, we're caching names and we got a name.
			 */
			if (!is_dn) {
				MEM(vp = fr_pair_afrom_da(list_ctx, inst->cache_da));
				fr_pair_value_bstrncpy(vp, values[i]->bv_val, values[i]->bv_len);
				fr_cursor_insert(&groups_cursor, vp);
			/*
			 *	We were told to cache names but we got a DN, we now need to resolve
			 *	this to a name.
			 *	Only Active Directory supports filtering on DN, so we have to search
			 *	for each individual group.
			 */
			} else {
				char *dn;

				dn = rlm_ldap_berval_to_string(value_ctx, values[i]);
				rcode = rlm_ldap_group_dn2name(inst, request, pconn, dn, &name);
				talloc_free(dn);
				if (rcode != RLM_MODULE_OK) {
					ldap_value_free_len(values);
					talloc_free(value_ctx);
					fr_pair_list_free(&groups);

					return rcode;
				}

				MEM(vp = fr_pair_afrom_da(list_ctx, inst->cache_da));
				fr_pair_value_bstrncpy(vp, name, talloc_array_length(name) - 1);
				fr_cursor_insert(&groups_cursor, vp);
				talloc_free(name);
			}
		}
	}
	*name_p = NULL;

	rcode = rlm_ldap_group_name2dn(inst, request, pconn, group_name, group_dn, sizeof(group_dn));

	ldap_value_free_len(values);
	talloc_free(value_ctx);

	if (rcode != RLM_MODULE_OK) return rcode;

	fr_cursor_init(&list_cursor, list);

	RDEBUG("Adding cacheable user object memberships");
	RINDENT();
	if (RDEBUG_ENABLED) {
		for (vp = fr_cursor_first(&groups_cursor);
		     vp;
		     vp = fr_cursor_next(&groups_cursor)) {
			RDEBUG("&control:%s += \"%s\"", inst->cache_da->name, vp->vp_strvalue);
		}
	}

	fr_cursor_merge(&list_cursor, groups);

	for (dn_p = group_dn; *dn_p; dn_p++) {
		MEM(vp = fr_pair_afrom_da(list_ctx, inst->cache_da));
		fr_pair_value_strcpy(vp, *dn_p);
		fr_cursor_insert(&list_cursor, vp);

		RDEBUG("&control:%s += \"%s\"", inst->cache_da->name, vp->vp_strvalue);
		ldap_memfree(*dn_p);
	}
	REXDENT();

	return rcode;
}
/** 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, *last;
	home_server *home;
	REALM *realm;
	home_pool_t *pool;
	RADIUS_PACKET *packet = NULL;

	last = request->config_items;

	/*
	 *	Send as many packets as necessary to different
	 *	destinations.
	 */
	while (1) {
		vp = pairfind(last, PW_REPLICATE_TO_REALM, 0, TAG_ANY);
		if (!vp) break;

		last = vp->next;

		realm = realm_find2(vp->vp_strvalue);
		if (!realm) {
			RDEBUG2E("Cannot Replicate to unknown realm %s", realm);
			continue;
		}
		
		/*
		 *	We shouldn't really do this on every loop.
		 */
		switch (request->packet->code) {
		default:
			RDEBUG2E("Cannot replicate unknown packet code %d",
				request->packet->code);
			cleanup(packet);
			return RLM_MODULE_FAIL;
		
		case PW_AUTHENTICATION_REQUEST:
			pool = realm->auth_pool;
			break;
			
#ifdef WITH_ACCOUNTING
			
		case PW_ACCOUNTING_REQUEST:
			pool = realm->acct_pool;
			break;
#endif
			
#ifdef WITH_COA
		case PW_COA_REQUEST:
		case PW_DISCONNECT_REQUEST:
			pool = realm->acct_pool;
			break;
#endif
		}
		
		if (!pool) {
			RDEBUG2W("Cancelling replication to Realm %s, as the realm is local.", realm->name);
			continue;
		}
		
		home = home_server_ldb(realm->name, pool, request);
		if (!home) {
			RDEBUG2E("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(NULL, 1);
			if (!packet) return RLM_MODULE_FAIL;
			packet->sockfd = -1;
			packet->code = code;
			packet->id = fr_rand() & 0xff;

			packet->sockfd = fr_socket(&home->src_ipaddr, 0);
			if (packet->sockfd < 0) {
				RDEBUGE("Failed opening socket: %s", fr_strerror());
				rcode = RLM_MODULE_FAIL;
				goto done;
			}
			
			vps = radius_list(request, list);
			if (!vps) {
				RDEBUGW("List '%s' doesn't exist for "
				       "this packet", fr_int2str(pair_lists,
				       list, "?unknown?"));
				rcode = RLM_MODULE_INVALID;
				goto done;
			}
			
			/*
			 *	Don't assume the list actually contains any
			 *	attributes.
			 */
			if (*vps) {
				packet->vps = paircopy(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_AUTHENTICATION_REQUEST) &&
			    (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) != NULL) &&
			    (pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL)) {
				vp = radius_paircreate(request, &packet->vps,
						       PW_CHAP_CHALLENGE, 0);
				vp->length = AUTH_VECTOR_LEN;
				memcpy(vp->vp_strvalue, 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++;
			free(packet->data);
			packet->data = NULL;
			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, "¿unknown?"),realm->name);
		if (rad_send(packet, NULL, home->secret) < 0) {
			RDEBUGE("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;
}
Beispiel #15
0
/** Convert group membership information into attributes
 *
 * @param[in] inst rlm_ldap configuration.
 * @param[in] request Current request.
 * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect.
 * @param[in] entry retrieved by rlm_ldap_find_user or rlm_ldap_search.
 * @param[in] attr membership attribute to look for in the entry.
 * @return One of the RLM_MODULE_* values.
 */
rlm_rcode_t rlm_ldap_cacheable_userobj(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn,
				       LDAPMessage *entry, char const *attr)
{
	rlm_rcode_t rcode = RLM_MODULE_OK;

	struct berval **values;
	size_t value_len = 0;
	TALLOC_CTX *value_pool;

	char *group_name[LDAP_MAX_CACHEABLE + 1];
	char **name_p = group_name;

	char *group_dn[LDAP_MAX_CACHEABLE + 1];
	char **dn_p;

	char *name;

	VALUE_PAIR *vp, **vps;
	TALLOC_CTX *ctx;
	vp_cursor_t cursor;

	int is_dn, i, count;

	rad_assert(entry);
	rad_assert(attr);

	/*
	 *	Parse the membership information we got in the initial user query.
	 */
	values = ldap_get_values_len((*pconn)->handle, entry, attr);
	if (!values) {
		RDEBUG2("No cacheable group memberships found in user object");

		return RLM_MODULE_OK;
	}
	count = ldap_count_values_len(values);

	vps = radius_list(request, PAIR_LIST_CONTROL);
	ctx = radius_list_ctx(request, PAIR_LIST_CONTROL);
	fr_cursor_init(&cursor, vps);

	/*
	 *	Avoid allocing buffers for each value.
	 *
	 *	The old code used ldap_get_values, which was likely doing
	 *	a very similar thing internally to produce \0 terminated
	 *	buffers from bervalues.
	 */
	for (i = 0; (i < LDAP_MAX_CACHEABLE) && (i < count); i++) value_len += values[i]->bv_len + 1;
	value_pool = talloc_pool(request, value_len);

	for (i = 0; (i < LDAP_MAX_CACHEABLE) && (i < count); i++) {
		is_dn = rlm_ldap_is_dn(values[i]->bv_val, values[i]->bv_len);

		if (inst->cacheable_group_dn) {
			/*
			 *	The easy case, we're caching DNs and we got a DN.
			 */
			if (is_dn) {
				MEM(vp = pairalloc(ctx, inst->cache_da));
				pairstrncpy(vp, values[i]->bv_val, values[i]->bv_len);
				fr_cursor_insert(&cursor, vp);

				RDEBUG("Added %s with value \"%s\" to control list", inst->cache_da->name,
				       vp->vp_strvalue);
			/*
			 *	We were told to cache DNs but we got a name, we now need to resolve
			 *	this to a DN. Store all the group names in an array so we can do one query.
			 */
			} else {
				*name_p++ = rlm_ldap_berval_to_string(value_pool, values[i]);
			}
		}

		if (inst->cacheable_group_name) {
			/*
			 *	The easy case, we're caching names and we got a name.
			 */
			if (!is_dn) {
				MEM(vp = pairalloc(ctx, inst->cache_da));
				pairstrncpy(vp, values[i]->bv_val, values[i]->bv_len);
				fr_cursor_insert(&cursor, vp);

				RDEBUG("Added control:%s with value \"%s\"", inst->cache_da->name,
				       vp->vp_strvalue);
			/*
			 *	We were told to cache names but we got a DN, we now need to resolve
			 *	this to a name.
			 *	Only Active Directory supports filtering on DN, so we have to search
			 *	for each individual group.
			 */
			} else {
				char *dn;

				dn = rlm_ldap_berval_to_string(value_pool, values[i]);
				rcode = rlm_ldap_group_dn2name(inst, request, pconn, dn, &name);
				talloc_free(dn);
				if (rcode != RLM_MODULE_OK) {
					ldap_value_free_len(values);
					talloc_free(value_pool);

					return rcode;
				}

				MEM(vp = pairalloc(ctx, inst->cache_da));
				pairstrncpy(vp, name, talloc_array_length(name) - 1);
				fr_cursor_insert(&cursor, vp);

				RDEBUG("Added control:%s with value \"%s\"", inst->cache_da->name, name);
				talloc_free(name);
			}
		}
	}
	*name_p = NULL;

	rcode = rlm_ldap_group_name2dn(inst, request, pconn, group_name, group_dn, sizeof(group_dn));

	ldap_value_free_len(values);
	talloc_free(value_pool);

	if (rcode != RLM_MODULE_OK) return rcode;

	dn_p = group_dn;
	while (*dn_p) {
		MEM(vp = pairalloc(ctx, inst->cache_da));
		pairstrcpy(vp, *dn_p);
		fr_cursor_insert(&cursor, vp);

		RDEBUG("Added control:%s with value \"%s\"", inst->cache_da->name, *dn_p);
		ldap_memfree(*dn_p);

		dn_p++;
	}

	return rcode;
}
Beispiel #16
0
/** Convert value_pair_map_t to VALUE_PAIR(s) and add them to a REQUEST.
 *
 * Takes a single value_pair_map_t, resolves request and list identifiers
 * to pointers in the current request, then attempts to retrieve module
 * specific value(s) using callback, and adds the resulting values to the
 * correct request/list.
 *
 * @param request The current request.
 * @param map specifying destination attribute and location and src identifier.
 * @param func to retrieve module specific values and convert them to
 *	VALUE_PAIRS.
 * @param ctx to be passed to func.
 * @param src name to be used in debugging if different from map value.
 * @return -1 if the operation failed, -2 in the source attribute wasn't valid, 0 on success.
 */
int radius_map2request(REQUEST *request, value_pair_map_t const *map,
		       UNUSED char const *src, radius_tmpl_getvalue_t func, void *ctx)
{
	int rcode;
	vp_cursor_t cursor;
	VALUE_PAIR **list, *vp, *head = NULL;
	char buffer[1024];

	if (radius_request(&request, map->dst->request) < 0) {
		REDEBUG("Mapping \"%s\" -> \"%s\" invalid in this context", map->src->name, map->dst->name);

		return -2;
	}

	list = radius_list(request, map->dst->list);
	if (!list) {
		REDEBUG("Mapping \"%s\" -> \"%s\" invalid in this context", map->src->name, map->dst->name);

		return -2;
	}


	/*
	 *	The callback should either return -1 to signify operations error, -2 when it can't find the
	 *	attribute or list being referenced, or 0 to signify success.
	 *	It may return "sucess", but still have no VPs to work with.
	 *	Only if it returned an error code should it not write anything to the head pointer.
	 */
	rcode = func(&head, request, map, ctx);
	if (rcode < 0) {
		rad_assert(!head);

		return rcode;
	}

	if (!head) return 0;

	VERIFY_VP(head);

	if (debug_flag) for (vp = paircursor(&cursor, &head); vp; vp = pairnext(&cursor)) {
		char *value;

		switch (map->src->type) {
			/*
			 *	Just print the value being assigned
			 */
			default:

			case VPT_TYPE_LITERAL:
				vp_prints_value(buffer, sizeof(buffer), vp, '\'');
				value = buffer;
				break;
			case VPT_TYPE_XLAT:
				vp_prints_value(buffer, sizeof(buffer), vp, '"');
				value = buffer;
				break;
			case VPT_TYPE_DATA:
				vp_prints_value(buffer, sizeof(buffer), vp, 0);
				value = buffer;
				break;
			/*
			 *	Just printing the value doesn't make sense, but we still
			 *	want to know what it was...
			 */
			case VPT_TYPE_LIST:
				vp_prints_value(buffer, sizeof(buffer), vp, '\'');
				value = talloc_asprintf(request, "&%s%s -> %s", map->src->name, vp->da->name, buffer);
				break;
			case VPT_TYPE_ATTR:
				vp_prints_value(buffer, sizeof(buffer), vp, '\'');
				value = talloc_asprintf(request, "&%s -> %s", map->src->name, buffer);
				break;
		}


		RDEBUG("\t\t%s %s %s", map->dst->name, fr_int2str(fr_tokens, vp->op, "<INVALID>"), value);

		if (value != buffer) talloc_free(value);
	}

	/*
	 *	Use pairmove so the operator is respected
	 */
	radius_pairmove(request, list, head);
	return 0;
}
Beispiel #17
0
/** Convert value_pair_map_t to VALUE_PAIR(s) and add them to a REQUEST.
 *
 * Takes a single value_pair_map_t, resolves request and list identifiers
 * to pointers in the current request, then attempts to retrieve module
 * specific value(s) using callback, and adds the resulting values to the
 * correct request/list.
 *
 * @param request The current request.
 * @param map specifying destination attribute and location and src identifier.
 * @param func to retrieve module specific values and convert them to
 *	VALUE_PAIRS.
 * @param ctx to be passed to func.
 * @param src name to be used in debugging if different from map value.
 * @return -1 if the operation failed, -2 in the source attribute wasn't valid, 0 on success.
 */
int radius_map2request(REQUEST *request, value_pair_map_t const *map,
		       UNUSED char const *src, radius_tmpl_getvalue_t func, void *ctx)
{
	int rcode, num;
	VALUE_PAIR **list, *vp, *head = NULL;
	REQUEST *context;
	TALLOC_CTX *parent;
	vp_cursor_t cursor;

	/*
	 *	Sanity check inputs.  We can have a list or attribute
	 *	as a destination.
	 */
	if ((map->dst->type != VPT_TYPE_LIST) &&
	    (map->dst->type != VPT_TYPE_ATTR)) {
		REDEBUG("Invalid mapping destination");
		return -2;
	}

	context = request;
	if (radius_request(&context, map->dst->request) < 0) {
		REDEBUG("Mapping \"%s\" -> \"%s\" invalid in this context", map->src->name, map->dst->name);
		return -2;
	}

	/*
	 *	If there's no CoA packet and we're updating it,
	 *	auto-allocate it.
	 */
	if (((map->dst->list == PAIR_LIST_COA) ||
	     (map->dst->list == PAIR_LIST_DM)) &&
	    !request->coa) {
		request_alloc_coa(context);
		if (map->dst->list == PAIR_LIST_COA) {
			context->coa->proxy->code = PW_CODE_COA_REQUEST;
		} else {
			context->coa->proxy->code = PW_CODE_DISCONNECT_REQUEST;
		}
	}

	list = radius_list(context, map->dst->list);
	if (!list) {
		REDEBUG("Mapping \"%s\" -> \"%s\" invalid in this context", map->src->name, map->dst->name);

		return -2;
	}

	parent = radius_list_ctx(context, map->dst->list);

	/*
	 *	The callback should either return -1 to signify operations error, -2 when it can't find the
	 *	attribute or list being referenced, or 0 to signify success.
	 *	It may return "sucess", but still have no VPs to work with.
	 */
	rcode = func(&head, request, map, ctx);
	if (rcode < 0) {
		rad_assert(!head);
		return rcode;
	}

	if (!head) return 0;

	/*
	 *	Reparent the VP
	 */
	for (vp = fr_cursor_init(&cursor, &head); vp; vp = fr_cursor_next(&cursor)) {

		VERIFY_VP(vp);
		if (debug_flag) debug_map(request, map, vp);

		(void) talloc_steal(parent, vp);
	}

	/*
	 *	List to list copies.
	 */
	if (map->dst->type == VPT_TYPE_LIST) {
		switch (map->op) {
		case T_OP_CMP_FALSE:
			rad_assert(head == NULL);
			pairfree(list);

			if (map->dst->list == PAIR_LIST_REQUEST) {
				context->username = NULL;
				context->password = NULL;
			}
			break;

		case T_OP_SET:
			if (map->src->type == VPT_TYPE_LIST) {
				pairfree(list);
				*list = head;
			} else {
		case T_OP_EQ:

				rad_assert(map->src->type == VPT_TYPE_EXEC);
				pairmove(parent, list, &head);
				pairfree(&head);
			}

			if (map->dst->list == PAIR_LIST_REQUEST) {
				context->username = pairfind(head, PW_USER_NAME, 0, TAG_ANY);
				context->password = pairfind(head, PW_USER_PASSWORD, 0, TAG_ANY);
			}
			break;

		case T_OP_ADD:
			pairadd(list, head);
			break;

		default:
			pairfree(&head);
			return -1;
		}

		return 0;
	}

	/*
	 *	We now should have only one destination attribute, and
	 *	only one source attribute.
	 */
	rad_assert(head->next == NULL);

	/*
	 *	Find the destination attribute.  We leave with either
	 *	the cursor and vp pointing to the attribute, or vp is
	 *	NULL.
	 */
	num = map->dst->num;
	for (vp = fr_cursor_init(&cursor, list);
	     vp != NULL;
	     vp = fr_cursor_next(&cursor)) {
		VERIFY_VP(vp);
		if ((vp->da == map->dst->da) && (!vp->da->flags.has_tag || (map->dst->tag == TAG_ANY) || (vp->tag == map->dst->tag))) {
			if (num == 0) break;
			num--;
		}
	}

	/*
	 *	Figure out what to do with the source attribute.
	 */
	switch (map->op) {
	case T_OP_CMP_FALSE:	/* remove matching attributes */
		pairfree(&head);
		if (!vp) return 0;

		/*
		 *	Wildcard: delete all of the matching ones,
		 *	based on tag.
		 */
		if (!map->dst->num) {
			pairdelete(list, map->dst->da->attr, map->dst->da->vendor,
				   map->dst->tag);
			vp = NULL;
		} else {
			/*
			 *	We've found the Nth one.  Delete it, and only
			 *	it.
			 */
			vp = fr_cursor_remove(&cursor);
		}

		/*
		 *	Check that the User-Name and User-Password
		 *	caches point to the correct attribute.
		 */
	fixup:
		if (map->dst->list == PAIR_LIST_REQUEST) {
			context->username = pairfind(*list, PW_USER_NAME, 0, TAG_ANY);
			context->password = pairfind(*list, PW_USER_PASSWORD, 0, TAG_ANY);
		}
		pairfree(&vp);
		return 0;

	case T_OP_EQ:		/* set only if not already set */
		if (vp) {
			pairfree(&head);
			return 0;
		}
		fr_cursor_insert(&cursor, head);
		goto fixup;

	case T_OP_SET:		/* over-write if existing, or else add */
		if (vp) vp = fr_cursor_remove(&cursor);
		fr_cursor_insert(&cursor, head);
		goto fixup;

	case T_OP_ADD:		/* append no matter what */
		vp = NULL;
		pairadd(list, head);
		goto fixup;

	case T_OP_SUB:		/* delete if it matches */
		head->op = T_OP_CMP_EQ;
		rcode = radius_compare_vps(NULL, head, vp);
		pairfree(&head);

		if (rcode == 0) {
			vp = fr_cursor_remove(&cursor);
			goto fixup;
		}
		return 0;

	default:		/* filtering operators */
		/*
		 *	If the VP doesn't exist, the filters will add
		 *	it with the given value.
		 */
		if (!vp) {
			fr_cursor_insert(&cursor, head);
			goto fixup;
		}
		break;
	}

	/*
	 *	The LHS exists.  We need to limit it's value based on
	 *	the operator, and the value of the RHS.
	 */
	head->op = map->op;
	rcode = radius_compare_vps(NULL, head, vp);
	head->op = T_OP_SET;

	switch (map->op) {
	case T_OP_CMP_EQ:
		if (rcode == 0) {
	leave:
			pairfree(&head);
			break;
		}
	replace:
		vp = fr_cursor_remove(&cursor);
		fr_cursor_insert(&cursor, head);
		goto fixup;

	case T_OP_LE:
		if (rcode <= 0) goto leave;
		goto replace;

	case T_OP_GE:
		if (rcode >= 0) goto leave;
		goto replace;

	default:
		pairfree(&head);
		return -1;
	}

	return 0;
}
Beispiel #18
0
/** Convert a map to a VALUE_PAIR.
 *
 * @param[out] out Where to write the VALUE_PAIR(s).
 * @param[in] request structure (used only for talloc)
 * @param[in] map the map. The LHS (dst) has to be VPT_TYPE_ATTR or VPT_TYPE_LIST.
 * @param[in] ctx unused
 * @return 0 on success, -1 on failure, -2 on attribute not found/equivalent
 */
int radius_map2vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, UNUSED void *ctx)
{
	int rcode = 0;
	VALUE_PAIR *vp = NULL, *found, **from = NULL;
	DICT_ATTR const *da;
	REQUEST *context;
	vp_cursor_t cursor;

	rad_assert(request != NULL);
	rad_assert(map != NULL);

	*out = NULL;

	/*
	 *	Special case for !*, we don't need to parse the value, just allocate an attribute with
	 *	the right operator.
	 */
	if (map->op == T_OP_CMP_FALSE) {
		vp = pairalloc(request, map->dst->da);
		if (!vp) return -1;
		vp->op = map->op;
		*out = vp;

		return 0;
	}

	/*
	 *	List to list found, this is a special case because we don't need
	 *	to allocate any attributes, just found the current list, and change
	 *	the op.
	 */
	if ((map->dst->type == VPT_TYPE_LIST) && (map->src->type == VPT_TYPE_LIST)) {
		from = radius_list(request, map->src->list);
		if (!from) return -2;

		found = paircopy(request, *from);
		/*
		 *	List to list copy is invalid if the src list has no attributes.
		 */
		if (!found) return -2;

		for (vp = paircursor(&cursor, &found);
		     vp;
		     vp = pairnext(&cursor)) {
		 	vp->op = T_OP_ADD;
		}

		*out = found;

		return 0;
	}

	/*
	 *	Deal with all non-list founding operations.
	 */
	da = map->dst->da ? map->dst->da : map->src->da;

	switch (map->src->type) {
	case VPT_TYPE_XLAT:
	case VPT_TYPE_LITERAL:
	case VPT_TYPE_DATA:
		vp = pairalloc(request, da);
		if (!vp) return -1;
		vp->op = map->op;
		break;
	default:
		break;
	}


	/*
	 *	And parse the RHS
	 */
	switch (map->src->type) {
	case VPT_TYPE_XLAT:
		rad_assert(map->dst->da);	/* Need to know where were going to write the new attribute */
		/*
		 *	Don't call unnecessary expansions
		 */
		if (strchr(map->src->name, '%') != NULL) {
			ssize_t slen;
			char *str = NULL;

			slen = radius_axlat(&str, request, map->src->name, NULL, NULL);
			if (slen < 0) {
				rcode = slen;
				goto error;
			}
			rcode = pairparsevalue(vp, str);
			talloc_free(str);
			if (!rcode) {
				pairfree(&vp);
				rcode = -1;
				goto error;
			}

			break;
		}
		/* FALL-THROUGH */

	case VPT_TYPE_LITERAL:
		if (!pairparsevalue(vp, map->src->name)) {
			rcode = -2;
			goto error;
		}
		break;

	case VPT_TYPE_ATTR:
		rad_assert(!map->dst->da ||
			   (map->src->da->type == map->dst->da->type) ||
			   (map->src->da->type == PW_TYPE_OCTETS) ||
			   (map->dst->da->type == PW_TYPE_OCTETS));
		context = request;

		if (radius_request(&context, map->src->request) == 0) {
			from = radius_list(context, map->src->list);
		}

		/*
		 *	Can't add the attribute if the list isn't
		 *	valid.
		 */
		if (!from) {
			rcode = -2;
			goto error;
		}

		/*
		 *	Special case, destination is a list, found all instance of an attribute.
		 */
		if (map->dst->type == VPT_TYPE_LIST) {
			found = paircopy2(request, *from, map->src->da->attr, map->src->da->vendor, TAG_ANY);
			if (!found) {
				REDEBUG("Attribute \"%s\" not found in request", map->src->name);
				rcode = -2;
				goto error;
			}

			for (vp = paircursor(&cursor, &found);
			     vp;
			     vp = pairnext(&cursor)) {
				vp->op = T_OP_ADD;
			}

			*out = found;
			return 0;
		}

		/*
		 *	FIXME: allow tag references?
		 */
		found = pairfind(*from, map->src->da->attr, map->src->da->vendor, TAG_ANY);
		if (!found) {
			REDEBUG("Attribute \"%s\" not found in request", map->src->name);
			rcode = -2;
			goto error;
		}

		/*
		 *	Copy the data over verbatim, assuming it's
		 *	actually data.
		 */
//		rad_assert(found->type == VT_DATA);
		vp = paircopyvpdata(request, da, found);
		if (!vp) {
			return -1;
		}
		vp->op = map->op;

		break;

	case VPT_TYPE_DATA:
		rad_assert(map->src->da->type == map->dst->da->type);
		memcpy(&vp->data, map->src->vpd, sizeof(vp->data));
		vp->length = map->src->length;
		break;

	/*
	 *	This essentially does the same as rlm_exec xlat, except it's non-configurable.
	 *	It's only really here as a convenience for people who expect the contents of
	 *	backticks to be executed in a shell.
	 *
	 *	exec string is xlat expanded and arguments are shell escaped.
	 */
	case VPT_TYPE_EXEC:
		return radius_mapexec(out, request, map);
	default:
		rad_assert(0);	/* Should of been caught at parse time */
	error:
		pairfree(&vp);
		return rcode;
	}

	*out = vp;
	return 0;
}
Beispiel #19
0
/** Convert a map to a VALUE_PAIR.
 *
 * @param[out] out Where to write the VALUE_PAIR(s).
 * @param[in] request structure (used only for talloc)
 * @param[in] map the map. The LHS (dst) has to be VPT_TYPE_ATTR or VPT_TYPE_LIST.
 * @param[in] ctx unused
 * @return 0 on success, -1 on failure, -2 on attribute not found/equivalent
 */
int radius_map2vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, UNUSED void *ctx)
{
	int rcode = 0;
	VALUE_PAIR *vp = NULL, *found, **from = NULL;
	DICT_ATTR const *da;
	REQUEST *context = request;
	vp_cursor_t cursor;

	rad_assert(request != NULL);
	rad_assert(map != NULL);

	*out = NULL;

	/*
	 *	Special case for !*, we don't need to parse RHS as this is a unary operator.
	 */
	if (map->op == T_OP_CMP_FALSE) {
		/*
		 *  Were deleting all the attributes in a list. This isn't like the other
		 *  mappings because lists aren't represented as attributes (yet),
		 *  so we can't return a <list> attribute with the !* operator for
		 *  radius_pairmove() to consume, and need to do the work here instead.
		 */
		if (map->dst->type == VPT_TYPE_LIST) {
			if (radius_request(&context, map->dst->request) == 0) {
				from = radius_list(context, map->dst->list);
			}
			if (!from) return -2;

			pairfree(from);

			/* @fixme hacky! */
			if (map->dst->list == PAIR_LIST_REQUEST) {
				context->username = NULL;
				context->password = NULL;
			}

			return 0;
		}

		/* Not a list, but an attribute, radius_pairmove() will perform that actual delete */
		vp = pairalloc(request, map->dst->da);
		if (!vp) return -1;
		vp->op = map->op;
		*out = vp;

		return 0;
	}

	/*
	 *	List to list found, this is a special case because we don't need
	 *	to allocate any attributes, just finding the current list, and change
	 *	the op.
	 */
	if ((map->dst->type == VPT_TYPE_LIST) && (map->src->type == VPT_TYPE_LIST)) {
		if (radius_request(&context, map->src->request) == 0) {
			from = radius_list(context, map->src->list);
		}
		if (!from) return -2;

		found = paircopy(request, *from);
		/*
		 *	List to list copy is invalid if the src list has no attributes.
		 */
		if (!found) return -2;

		for (vp = fr_cursor_init(&cursor, &found);
		     vp;
		     vp = fr_cursor_next(&cursor)) {
		 	vp->op = T_OP_ADD;
		}

		*out = found;

		return 0;
	}

	/*
	 *	Deal with all non-list operations.
	 */
	da = map->dst->da ? map->dst->da : map->src->da;

	switch (map->src->type) {
	case VPT_TYPE_XLAT:
	case VPT_TYPE_XLAT_STRUCT:
	case VPT_TYPE_LITERAL:
	case VPT_TYPE_DATA:
		vp = pairalloc(request, da);
		if (!vp) return -1;
		vp->op = map->op;
		break;
	default:
		break;
	}


	/*
	 *	And parse the RHS
	 */
	switch (map->src->type) {
		ssize_t slen;
		char *str;

	case VPT_TYPE_XLAT_STRUCT:
		rad_assert(map->dst->da);	/* Need to know where were going to write the new attribute */
		rad_assert(map->src->xlat != NULL);

		str = NULL;
		slen = radius_axlat_struct(&str, request, map->src->xlat, NULL, NULL);
		if (slen < 0) {
			rcode = slen;
			goto error;
		}

		/*
		 *	We do the debug printing because radius_axlat_struct
		 *	doesn't have access to the original string.  It's been
		 *	mangled during the parsing to xlat_exp_t
		 */
		RDEBUG2("EXPAND %s", map->src->name);
		RDEBUG2("   --> %s", str);

		rcode = pairparsevalue(vp, str);
		talloc_free(str);
		if (!rcode) {
			pairfree(&vp);
			rcode = -1;
			goto error;
		}
		break;

	case VPT_TYPE_XLAT:
		rad_assert(map->dst->da);	/* Need to know where were going to write the new attribute */

		str = NULL;
		slen = radius_axlat(&str, request, map->src->name, NULL, NULL);
		if (slen < 0) {
			rcode = slen;
			goto error;
		}
		rcode = pairparsevalue(vp, str);
		talloc_free(str);
		if (!rcode) {
			pairfree(&vp);
			rcode = -1;
			goto error;
		}
		break;

	case VPT_TYPE_LITERAL:
		if (!pairparsevalue(vp, map->src->name)) {
			rcode = -2;
			goto error;
		}
		break;

	case VPT_TYPE_ATTR:
		rad_assert(!map->dst->da ||
			   (map->src->da->type == map->dst->da->type) ||
			   (map->src->da->type == PW_TYPE_OCTETS) ||
			   (map->dst->da->type == PW_TYPE_OCTETS));

		/*
		 *	Special case, destination is a list, found all instance of an attribute.
		 */
		if (map->dst->type == VPT_TYPE_LIST) {
			context = request;

			if (radius_request(&context, map->src->request) == 0) {
				from = radius_list(context, map->src->list);
			}

			/*
			 *	Can't add the attribute if the list isn't
			 *	valid.
			 */
			if (!from) {
				rcode = -2;
				goto error;
			}

			found = paircopy2(request, *from, map->src->da->attr, map->src->da->vendor, TAG_ANY);
			if (!found) {
				REDEBUG("Attribute \"%s\" not found in request", map->src->name);
				rcode = -2;
				goto error;
			}

			for (vp = fr_cursor_init(&cursor, &found);
			     vp;
			     vp = fr_cursor_next(&cursor)) {
				vp->op = T_OP_ADD;
			}

			*out = found;
			return 0;
		}

		if (radius_vpt_get_vp(&found, request, map->src) < 0) {
			REDEBUG("Attribute \"%s\" not found in request", map->src->name);
			rcode = -2;
			goto error;
		}

		/*
		 *	Copy the data over verbatim, assuming it's
		 *	actually data.
		 */
		vp = paircopyvpdata(request, da, found);
		if (!vp) {
			return -1;
		}
		vp->op = map->op;

		break;

	case VPT_TYPE_DATA:
		rad_assert(map->src && map->src->da);
		rad_assert(map->dst && map->dst->da);
		rad_assert(map->src->da->type == map->dst->da->type);
		memcpy(&vp->data, map->src->vpd, sizeof(vp->data));
		vp->length = map->src->length;
		break;

	/*
	 *	This essentially does the same as rlm_exec xlat, except it's non-configurable.
	 *	It's only really here as a convenience for people who expect the contents of
	 *	backticks to be executed in a shell.
	 *
	 *	exec string is xlat expanded and arguments are shell escaped.
	 */
	case VPT_TYPE_EXEC:
		return radius_mapexec(out, request, map);
	default:
		rad_assert(0);	/* Should of been caught at parse time */
	error:
		pairfree(&vp);
		return rcode;
	}

	*out = vp;
	return 0;
}
Beispiel #20
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;
}
Beispiel #21
0
/** Return a VP from a value_pair_tmpl_t
 *
 * @param out where to write the retrieved vp.
 * @param request current request.
 * @param vpt the value pair template
 * @return -1 if VP could not be found, -2 if list could not be found, -3 if context could not be found.
 */
int radius_vpt_get_vp(VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt)
{
	VALUE_PAIR **vps, *vp;

	if (out) *out = NULL;

	if (radius_request(&request, vpt->request) < 0) {
		return -3;
	}

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

	switch (vpt->type) {
		/*
		 *	May not may not be found, but it *is* a known
		 *	name.
		 */
	case VPT_TYPE_ATTR:
		if (vpt->num == 0) {
			vp = pairfind(*vps, vpt->da->attr, vpt->da->vendor, vpt->tag);
			if (!vp) return -1;

		} else {
			int num;
			vp_cursor_t cursor;

			/*
			 *	It's faster to just repeat the 3-4 lines of pairfind here.
			 */
			num = vpt->num;
			for (vp = fr_cursor_init(&cursor, vps);
			     vp != NULL;
			     vp = fr_cursor_next(&cursor)) {
				VERIFY_VP(vp);
				if ((vp->da == vpt->da) && (!vp->da->flags.has_tag || (vpt->tag == TAG_ANY) || (vp->tag == vpt->tag))) {
					if (num == 0) {
						*out = vp;
						return 0;
					}
					num--;
				}
			}
			return -1;
		}
		break;

	case VPT_TYPE_LIST:
		vp = *vps;
		break;

	default:
		/*
		 *	literal, xlat, regex, exec, data.
		 *	no attribute.
		 */
		return -1;
	}

	if (out) {
		*out = vp;
	}

	return 0;
}
Beispiel #22
0
/*
 *  Dispatch an exec method
 */
static rlm_rcode_t exec_dispatch(void *instance, REQUEST *request)
{
	rlm_exec_t	*inst = (rlm_exec_t *)instance;
	rlm_rcode_t	rcode;
	int		status;

	VALUE_PAIR	**input_pairs = NULL, **output_pairs = NULL;
	VALUE_PAIR	*answer = NULL;
	char		out[1024];

	/*
	 *	We need a program to execute.
	 */
	if (!inst->program) {
		ERROR("rlm_exec (%s): We require a program to execute", inst->xlat_name);
		return RLM_MODULE_FAIL;
	}

	/*
	 *	See if we're supposed to execute it now.
	 */
	if (!((inst->packet_code == 0) || (request->packet->code == inst->packet_code) ||
	      (request->reply->code == inst->packet_code)
#ifdef WITH_PROXY
	      || (request->proxy && (request->proxy->code == inst->packet_code)) ||
	      (request->proxy_reply && (request->proxy_reply->code == inst->packet_code))
#endif
		    )) {
		RDEBUG2("Packet type is not %s. Not executing.", inst->packet_type);

		return RLM_MODULE_NOOP;
	}

	/*
	 *	Decide what input/output the program takes.
	 */
	if (inst->input) {
		input_pairs = radius_list(request, inst->input_list);
		if (!input_pairs) {
			return RLM_MODULE_INVALID;
		}
	}

	if (inst->output) {
		output_pairs = radius_list(request, inst->output_list);
		if (!output_pairs) {
			return RLM_MODULE_INVALID;
		}
	}

	/*
	 *	This function does it's own xlat of the input program
	 *	to execute.
	 */
	status = radius_exec_program(request, inst->program, inst->wait, inst->shell_escape,
				     out, sizeof(out), inst->timeout,
				     inst->input ? *input_pairs : NULL,
				     inst->output ? &answer : NULL);
	rcode = rlm_exec_status2rcode(request, out, strlen(out), status);

	/*
	 *	Move the answer over to the output pairs.
	 *
	 *	If we're not waiting, then there are no output pairs.
	 */
	if (inst->output) {
		pairmove(request, output_pairs, &answer);
	}
	pairfree(&answer);

	return rcode;
}
Beispiel #23
0
/** Process map which has exec as a src
 *
 * Evaluate maps which specify exec as a src. This may be used by various sorts of update sections, and so
 * has been broken out into it's own function.
 *
 * @param[out] out Where to write the VALUE_PAIR(s).
 * @param[in] request structure (used only for talloc).
 * @param[in] map the map. The LHS (dst) must be VPT_TYPE_ATTR or VPT_TYPE_LIST. The RHS (src) must be VPT_TYPE_EXEC.
 * @return -1 on failure, 0 on success.
 */
int radius_mapexec(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map)
{
	int result;
	char *expanded = NULL;
	char answer[1024];
	VALUE_PAIR **input_pairs = NULL;
	VALUE_PAIR **output_pairs = NULL;

	*out = NULL;

	rad_assert(map->src->type == VPT_TYPE_EXEC);
	rad_assert((map->dst->type == VPT_TYPE_ATTR) || (map->dst->type == VPT_TYPE_LIST));

	/*
	 *	We always put the request pairs into the environment
	 */
	input_pairs = radius_list(request, PAIR_LIST_REQUEST);

	/*
	 *	Automagically switch output type depending on our destination
	 *	If dst is a list, then we create attributes from the output of the program
	 *	if dst is an attribute, then we create an attribute of that type and then
	 *	call pairparsevalue on the output of the script.
	 */
	out[0] = '\0';
	result = radius_exec_program(request, map->src->name, true, true,
				     answer, sizeof(answer),
				     input_pairs ? *input_pairs : NULL,
				     (map->dst->type == VPT_TYPE_LIST) ? output_pairs : NULL);
	talloc_free(expanded);
	if (result != 0) {
		REDEBUG("%s", answer);
		talloc_free(output_pairs);
		return -1;
	}

	switch (map->dst->type) {
	case VPT_TYPE_LIST:
		if (!output_pairs) {
			return -2;
		}
		*out = *output_pairs;

		return 0;
	case VPT_TYPE_ATTR:
		{
			VALUE_PAIR *vp;

			vp = pairalloc(request, map->dst->da);
			if (!vp) return -1;
			vp->op = map->op;
			if (!pairparsevalue(vp, answer)) {
				pairfree(&vp);
				return -2;
			}
			*out = vp;

			return 0;
		}
	default:
		rad_assert(0);
	}

	return -1;
}
Beispiel #24
0
/** Expand values in an attribute map where needed
 *
 */
int rlm_ldap_map_xlat(REQUEST *request, value_pair_map_t const *maps, rlm_ldap_map_xlat_t *expanded)
{
	value_pair_map_t const *map;
	unsigned int total = 0;

	VALUE_PAIR *found, **from = NULL;
	REQUEST *context;

	for (map = maps; map != NULL; map = map->next) {
		switch (map->src->type) {
		case VPT_TYPE_XLAT:
		{
			ssize_t len;
			char *exp = NULL;

			len = radius_xlat(exp, 0, request, map->src->name, NULL, NULL);
			if (len < 0) {
				RDEBUG("Expansion of LDAP attribute \"%s\" failed", map->src->name);

				goto error;
			}

			expanded->attrs[total++] = exp;
			break;
		}

		case VPT_TYPE_ATTR:
			context = request;

			if (radius_request(&context, map->src->vpt_request) == 0) {
				from = radius_list(context, map->src->vpt_list);
			}
			if (!from) continue;

			found = pairfind(*from, map->src->vpt_da->attr, map->src->vpt_da->vendor, TAG_ANY);
			if (!found) continue;

			expanded->attrs[total++] = talloc_typed_strdup(request, found->vp_strvalue);
			break;

		case VPT_TYPE_EXEC:
		{
			char answer[1024];
			VALUE_PAIR **input_pairs = NULL;
			int result;

			input_pairs = radius_list(request, PAIR_LIST_REQUEST);
			result = radius_exec_program(request, map->src->name, true, true, answer,
						     sizeof(answer), EXEC_TIMEOUT,
						     input_pairs ? *input_pairs : NULL, NULL);
			if (result != 0) {
				return -1;
			}

			expanded->attrs[total++] = talloc_typed_strdup(request, answer);
		}
			break;

		case VPT_TYPE_LITERAL:
			expanded->attrs[total++] = map->src->name;
			break;

		default:
			rad_assert(0);
		error:
			expanded->attrs[total] = NULL;

			rlm_ldap_map_xlat_free(expanded);

			return -1;
		}
	}

	rad_assert(total < LDAP_MAX_ATTRMAP);

	expanded->attrs[total] = NULL;
	expanded->count = total;
	expanded->maps = maps;

	return 0;
}
Beispiel #25
0
/** Copy pairs matching a VPT in the current request
 *
 * @param ctx to allocate new VALUE_PAIRs under.
 * @param out where to write the copied vps.
 * @param request current request.
 * @param vpt the value pair template
 * @return -1 if VP could not be found, -2 if list could not be found, -3 if context could not be found.
 */
int tmpl_copy_vps(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt)
{
	VALUE_PAIR **vps, *vp;
	REQUEST *current = request;
	vp_cursor_t from, to;

	rad_assert((vpt->type == TMPL_TYPE_ATTR) || (vpt->type == TMPL_TYPE_LIST));

	if (out) *out = NULL;

	if (radius_request(&current, vpt->tmpl_request) < 0) {
		return -3;
	}

	vps = radius_list(request, vpt->tmpl_list);
	if (!vps) {
		return -2;
	}

	switch (vpt->type) {
	/*
	 *	May not be found, but it *is* a known name.
	 */
	case TMPL_TYPE_ATTR:
	{
		int num;

		(void) fr_cursor_init(&to, out);
		(void) fr_cursor_init(&from, vps);

		vp = fr_cursor_next_by_da(&from, vpt->tmpl_da, vpt->tmpl_tag);
		if (!vp) return -1;

		switch (vpt->tmpl_num) {
		/* Copy all pairs of this type (and tag) */
		case NUM_ALL:
			do {
				VERIFY_VP(vp);
				vp = paircopyvp(ctx, vp);
				if (!vp) {
					pairfree(out);
					return -4;
				}
				fr_cursor_insert(&to, vp);
			} while ((vp = fr_cursor_next_by_da(&from, vpt->tmpl_da, vpt->tmpl_tag)));
			break;

		/* Specific attribute number */
		default:
			for (num = vpt->tmpl_num;
			     num && vp;
			     num--, vp = fr_cursor_next_by_da(&from, vpt->tmpl_da, vpt->tmpl_tag)) {
			     VERIFY_VP(vp);
			}
			if (!vp) return -1;
			/* FALL-THROUGH */

		/* Just copy the first pair */
		case NUM_ANY:
			vp = paircopyvp(ctx, vp);
			if (!vp) {
				pairfree(out);
				return -4;
			}
			fr_cursor_insert(&to, vp);
		}
	}
		break;

	case TMPL_TYPE_LIST:
		vp = paircopy(ctx, *vps);
		if (!vp) return 0;

		fr_cursor_merge(&to, vp);
		break;

	default:
		rad_assert(0);
	}

	return 0;
}