Exemple #1
0
/** Extract a named subcapture value from the request
 *
 * @note This is the PCRE variant of the function.
 *
 * @param[in] ctx	To allocate subcapture buffer in.
 * @param[out] out	Where to write the subcapture string.
 * @param[in] request	to extract.
 * @param[in] name	of subcapture.
 * @return
 *	- 0 on success.
 *	- -1 on notfound.
 */
int regex_request_to_sub_named(TALLOC_CTX *ctx, char **out, REQUEST *request, char const *name)
{
	fr_regcapture_t	*rc;
	char const	*p;
	int		ret;

	rc = request_data_reference(request, request, REQUEST_DATA_REGEX);
	if (!rc) {
		RDEBUG4("No subcapture data found");
		*out = NULL;
		return 1;
	}

	ret = pcre_get_named_substring(rc->preg->compiled, rc->regmatch->subject,
				       (int *)rc->regmatch->match_data, (int)rc->regmatch->used, name, &p);
	switch (ret) {
	case PCRE_ERROR_NOMEMORY:
		MEM(NULL);
		/*
		 *	We can't really fall through, but GCC 7.3 is
		 *	too stupid to realise that we can never get
		 *	here despite _fr_exit_now being marked as
		 *	NEVER_RETURNS.
		 *
		 *	If we did anything else, compilers and static
		 *	analysis tools would probably complain about
		 *	code that could never be executed *sigh*.
		 */
		/* FALL-THROUGH */
	/*
	 *	Not finding a substring is fine
	 */
	case PCRE_ERROR_NOSUBSTRING:
		RDEBUG4("No named capture group \"%s\"", name);
		*out = NULL;
		return -1;

	default:
		if (ret < 0) {
			*out = NULL;
			return -1;
		}

		/*
		 *	Check libpcre really is using our overloaded
		 *	memory allocation and freeing talloc wrappers.
		 */
		talloc_set_type(p, char);
		talloc_steal(ctx, p);

		RDEBUG4("Found \"%s\": %pV (%zu)", name,
			fr_box_strvalue_buffer(p), talloc_array_length(p) - 1);

		memcpy(out, &p, sizeof(*out));

		break;
	}

	return 0;
}
Exemple #2
0
/** Extract a subcapture value from the request
 *
 * @note This is the PCRE variant of the function.
 *
 * @param[in] ctx	To allocate subcapture buffer in.
 * @param[out] out	Where to write the subcapture string.
 * @param[in] request	to extract.
 * @param[in] num	Subcapture index (0 for entire match).
 * @return
 *	- 0 on success.
 *	- -1 on notfound.
 */
int regex_request_to_sub(TALLOC_CTX *ctx, char **out, REQUEST *request, uint32_t num)
{
	fr_regcapture_t		*rc;
	char			*buff;
	size_t			len;
	int			ret;
	pcre2_match_data	*match_data;

	rc = request_data_reference(request, request, REQUEST_DATA_REGEX);
	if (!rc) {
		RDEBUG4("No subcapture data found");
		*out = NULL;
		return 1;
	}
	match_data = talloc_get_type_abort(rc->regmatch->match_data, pcre2_match_data);

	ret = pcre2_substring_length_bynumber(match_data, num, &len);
	switch (ret) {
	case PCRE2_ERROR_NOMEMORY:
		MEM(NULL);
		/*
		 *	We can't really fall through, but GCC 7.3 is
		 *	too stupid to realise that we can never get
		 *	here despite _fr_exit_now being marked as
		 *	NEVER_RETURNS.
		 *
		 *	If we did anything else, compilers and static
		 *	analysis tools would probably complain about
		 *	code that could never be executed *sigh*.
		 */
		/* FALL-THROUGH */

	/*
	 *	Not finding a substring is fine
	 */
	case PCRE2_ERROR_NOSUBSTRING:
		RDEBUG4("%i/%zu Not found", num + 1, rc->regmatch->used);
		*out = NULL;
		return -1;

	default:
		if (ret < 0) {
			*out = NULL;
			return -1;
		}

		MEM(buff = talloc_array(ctx, char, ++len));	/* +1 for \0, it'll get reset by pcre2_substring */
		pcre2_substring_copy_bynumber(match_data, num, (PCRE2_UCHAR *)buff, &len); /* can't error */

		RDEBUG4("%i/%zu Found: %pV (%zu)", num + 1, rc->regmatch->used,
			fr_box_strvalue_buffer(buff), talloc_array_length(buff) - 1);

		*out = buff;
		break;
	}

	return 0;
}
Exemple #3
0
/** Implements the Foreach-Variable-X
 *
 * @see modcall()
 */
static ssize_t xlat_foreach(void *instance, REQUEST *request,
			    UNUSED char const *fmt, char *out, size_t outlen)
{
	VALUE_PAIR	**pvp;

	/*
	 *	See modcall, "FOREACH" for how this works.
	 */
	pvp = (VALUE_PAIR **) request_data_reference(request, radius_get_vp, *(int*) instance);
	if (!pvp || !*pvp) {
		*out = '\0';
		return 0;
	}

	return valuepair2str(out, outlen, (*pvp), (*pvp)->da->type);
}
Exemple #4
0
/** Extract a subcapture value from the request
 *
 * @note This is the POSIX variant of the function.
 *
 * @param[in] ctx	To allocate subcapture buffer in.
 * @param[out] out	Where to write the subcapture string.
 * @param[in] request	to extract.
 * @param[in] num	Subcapture index (0 for entire match).
 * @return
 *	- 0 on success.
 *	- -1 on notfound.
 */
int regex_request_to_sub(TALLOC_CTX *ctx, char **out, REQUEST *request, uint32_t num)
{
	fr_regcapture_t	*rc;
	char 		*buff;
	char const	*start;
	size_t		len;
	regmatch_t	*match_data;

	rc = request_data_reference(request, request, REQUEST_DATA_REGEX);
	if (!rc) {
		RDEBUG4("No subcapture data found");
		*out = NULL;
		return -1;
	}
	match_data = rc->regmatch->match_data;

	/*
	 *	Greater than our capture array
	 *
	 *	-1 means no value in this capture group.
	 */
	if ((num >= rc->regmatch->used) || (match_data[num].rm_eo == -1) || (match_data[num].rm_so == -1)) {
		RDEBUG4("%i/%zu Not found", num + 1, rc->regmatch->used);
		*out = NULL;
		return -1;
	}

	/*
	 *	Sanity checks on the offsets
	 */
	rad_assert(match_data[num].rm_eo <= (regoff_t)talloc_array_length(rc->regmatch->subject));
	rad_assert(match_data[num].rm_so <= (regoff_t)talloc_array_length(rc->regmatch->subject));

	start = rc->regmatch->subject + match_data[num].rm_so;
	len = match_data[num].rm_eo - match_data[num].rm_so;

	MEM(buff = talloc_bstrndup(ctx, start, len));
	RDEBUG4("%i/%zu Found: %pV (%zu)", num + 1, rc->regmatch->used, fr_box_strvalue_buffer(buff), len);

	*out = buff;

	return 0;
}
Exemple #5
0
/*
 * @brief Expand regexp matches %{0} to %{8}
 */
static size_t xlat_regex(void *instance, REQUEST *request,
			 const char *fmt, char *out, size_t outlen)
{
	char *regex;

	/*
	 *	We cheat: fmt is "0" to "8", but those numbers
	 *	are already in the "instance".
	 */
	fmt = fmt;		/* -Wunused */

	regex = request_data_reference(request, request,
				 REQUEST_DATA_REGEX | *(int *)instance);
	if (!regex) return 0;

	/*
	 *	Copy UP TO "freespace" bytes, including
	 *	a zero byte.
	 */
	strlcpy(out, regex, outlen);
	return strlen(out);
}
Exemple #6
0
static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * const node,
			 RADIUS_ESCAPE_STRING escape, void *escape_ctx, int lvl)
{
	ssize_t rcode;
	char *str = NULL, *child;
	REQUEST *ref;

	XLAT_DEBUG("%.*sxlat aprint %d", lvl, xlat_spaces, node->type);

	switch (node->type) {
		/*
		 *	Don't escape this.
		 */
	case XLAT_LITERAL:
		XLAT_DEBUG("xlat_aprint LITERAL");
		return talloc_strdup(ctx, node->fmt);

		/*
		 *	Do a one-character expansion.
		 */
	case XLAT_PERCENT:
	{
		char const *p;
		char *nl;
		size_t freespace = 256;
		struct tm ts;
		time_t when;

		XLAT_DEBUG("xlat_aprint PERCENT");

		str = talloc_array(ctx, char, freespace); /* @todo do better allocation */
		p = node->fmt;

		when = request->timestamp;
		if (request->packet) {
			when = request->packet->timestamp.tv_sec;
		}

		switch (*p) {
		case '%':
			str[0] = '%';
			str[1] = '\0';
			break;

		case 'd': /* request day */
			if (!localtime_r(&when, &ts)) goto error;
			strftime(str, freespace, "%d", &ts);
			break;

		case 'l': /* request timestamp */
			snprintf(str, freespace, "%lu",
				 (unsigned long) when);
			break;

		case 'm': /* request month */
			if (!localtime_r(&when, &ts)) goto error;
			strftime(str, freespace, "%m", &ts);
			break;

		case 't': /* request timestamp */
			CTIME_R(&when, str, freespace);
			nl = strchr(str, '\n');
			if (nl) *nl = '\0';
			break;

		case 'D': /* request date */
			if (!localtime_r(&when, &ts)) goto error;
			strftime(str, freespace, "%Y%m%d", &ts);
			break;

		case 'G': /* request minute */
			if (!localtime_r(&when, &ts)) goto error;
			strftime(str, freespace, "%M", &ts);
			break;

		case 'H': /* request hour */
			if (!localtime_r(&when, &ts)) goto error;
			strftime(str, freespace, "%H", &ts);
			break;

		case 'I': /* Request ID */
			if (request->packet) {
				snprintf(str, freespace, "%i", request->packet->id);
			}
			break;

		case 'S': /* request timestamp in SQL format*/
			if (!localtime_r(&when, &ts)) goto error;
			strftime(str, freespace, "%Y-%m-%d %H:%M:%S", &ts);
			break;

		case 'T': /* request timestamp */
			if (!localtime_r(&when, &ts)) goto error;
			strftime(str, freespace, "%Y-%m-%d-%H.%M.%S.000000", &ts);
			break;

		case 'Y': /* request year */
			if (!localtime_r(&when, &ts)) {
				error:
				REDEBUG("Failed converting packet timestamp to localtime: %s", fr_syserror(errno));
				talloc_free(str);
				return NULL;
			}
			strftime(str, freespace, "%Y", &ts);
			break;

		default:
			rad_assert(0 == 1);
			break;
		}
	}
		break;

	case XLAT_ATTRIBUTE:
		XLAT_DEBUG("xlat_aprint ATTRIBUTE");
		ref = request;
		if (radius_request(&ref, node->ref) < 0) {
			return NULL;
		}

		/*
		 *	Some attributes are virtual <sigh>
		 */
		str = xlat_getvp(ctx, ref, node->list, node->da, node->tag, node->num, true);
		if (str) {
			XLAT_DEBUG("expand attr %s --> '%s'", node->da->name, str);
		}
		break;

	case XLAT_VIRTUAL:
		XLAT_DEBUG("xlat_aprint VIRTUAL");
		str = talloc_array(ctx, char, 1024); /* FIXME: have the module call talloc_asprintf */
		rcode = node->xlat->func(node->xlat->instance, request, NULL, str, 1024);
		if (rcode < 0) {
			talloc_free(str);
			return NULL;
		}
		break;

	case XLAT_MODULE:
		XLAT_DEBUG("xlat_aprint MODULE");
		if (xlat_process(&child, request, node->child, node->xlat->escape, node->xlat->instance) == 0) {
			return NULL;
		}

		XLAT_DEBUG("%.*sexpand mod %s --> '%s'", lvl, xlat_spaces, node->fmt, child);

		str = talloc_array(ctx, char, 1024); /* FIXME: have the module call talloc_asprintf */
		*str = '\0';	/* Be sure the string is NULL terminated, we now only free on error */

		rcode = node->xlat->func(node->xlat->instance, request, child, str, 1024);
		talloc_free(child);
		if (rcode < 0) {
			talloc_free(str);
			return NULL;
		}
		break;

#ifdef HAVE_REGEX_H
	case XLAT_REGEX:
		XLAT_DEBUG("xlat_aprint REGEX");
		child = request_data_reference(request, request,
					       REQUEST_DATA_REGEX | node->num);
		if (!child) return NULL;

		str = talloc_strdup(ctx, child);
		break;
#endif

	case XLAT_ALTERNATE:
		XLAT_DEBUG("xlat_aprint ALTERNATE");
		rad_assert(node->child != NULL);
		rad_assert(node->alternate != NULL);

		str = xlat_aprint(ctx, request, node->child, escape, escape_ctx, lvl);
		if (str) break;

		str = xlat_aprint(ctx, request, node->alternate, escape, escape_ctx, lvl);
		break;

	}

	/*
	 *	Escape the non-literals we found above.
	 */
	if (str && escape) {
		char *escaped;

		escaped = talloc_array(ctx, char, 1024); /* FIXME: do something intelligent */
		escape(request, escaped, 1024, str, escape_ctx);
		talloc_free(str);
		str = escaped;
	}
Exemple #7
0
static unlang_action_t unlang_foreach(REQUEST *request,
				      rlm_rcode_t *presult, int *priority)
{
	VALUE_PAIR			*vp;
	unlang_stack_t			*stack = request->stack;
	unlang_stack_frame_t		*frame = &stack->frame[stack->depth];
	unlang_t			*instruction = frame->instruction;
	unlang_frame_state_foreach_t	*foreach = NULL;
	unlang_group_t			*g;

	g = unlang_generic_to_group(instruction);

	if (!frame->repeat) {
		int i, foreach_depth = -1;
		VALUE_PAIR *vps;

		if (stack->depth >= UNLANG_STACK_MAX) {
			ERROR("Internal sanity check failed: module stack is too deep");
			fr_exit(EXIT_FAILURE);
		}

		/*
		 *	Figure out how deep we are in nesting by looking at request_data
		 *	stored previously.
		 *
		 *	FIXME: figure this out by walking up the modcall stack instead.
		 */
		for (i = 0; i < 8; i++) {
			if (!request_data_reference(request, (void *)xlat_fmt_get_vp, i)) {
				foreach_depth = i;
				break;
			}
		}

		if (foreach_depth < 0) {
			REDEBUG("foreach Nesting too deep!");
			*presult = RLM_MODULE_FAIL;
			*priority = 0;
			return UNLANG_ACTION_CALCULATE_RESULT;
		}

		/*
		 *	Copy the VPs from the original request, this ensures deterministic
		 *	behaviour if someone decides to add or remove VPs in the set we're
		 *	iterating over.
		 */
		if (tmpl_copy_vps(stack, &vps, request, g->vpt) < 0) {	/* nothing to loop over */
			*presult = RLM_MODULE_NOOP;
			*priority = instruction->actions[RLM_MODULE_NOOP];
			return UNLANG_ACTION_CALCULATE_RESULT;
		}

		MEM(frame->state = foreach = talloc_zero(stack, unlang_frame_state_foreach_t));

		rad_assert(vps != NULL);

		foreach->depth = foreach_depth;
		foreach->vps = vps;
		fr_cursor_talloc_init(&foreach->cursor, &foreach->vps, VALUE_PAIR);
#ifndef NDEBUG
		foreach->indent = request->log.unlang_indent;
#endif

		vp = fr_cursor_head(&foreach->cursor);
	} else {
		foreach = talloc_get_type_abort(frame->state, unlang_frame_state_foreach_t);

		vp = fr_cursor_next(&foreach->cursor);

		/*
		 *	We've been asked to unwind to the
		 *	enclosing "foreach".  We're here, so
		 *	we can stop unwinding.
		 */
		if (frame->unwind == UNLANG_TYPE_BREAK) {
			frame->unwind = UNLANG_TYPE_NULL;
			vp = NULL;
		}

		/*
		 *	Unwind all the way.
		 */
		if (frame->unwind == UNLANG_TYPE_RETURN) {
			vp = NULL;
		}

		if (!vp) {
			/*
			 *	Free the copied vps and the request data
			 *	If we don't remove the request data, something could call
			 *	the xlat outside of a foreach loop and trigger a segv.
			 */
			fr_pair_list_free(&foreach->vps);
			request_data_get(request, (void *)xlat_fmt_get_vp, foreach->depth);

			*presult = frame->result;
			if (*presult != RLM_MODULE_UNKNOWN) *priority = instruction->actions[*presult];
#ifndef NDEBUG
			rad_assert(foreach->indent == request->log.unlang_indent);
#endif
			return UNLANG_ACTION_CALCULATE_RESULT;
		}
	}

#ifndef NDEBUG
	RDEBUG2("");
	RDEBUG2("# looping with: Foreach-Variable-%d = %pV", foreach->depth, &vp->data);
#endif

	rad_assert(vp);

	/*
	 *	Add the vp to the request, so that
	 *	xlat.c, xlat_foreach() can find it.
	 */
	foreach->variable = vp;
	request_data_add(request, (void *)xlat_fmt_get_vp, foreach->depth, &foreach->variable,
			 false, false, false);

	/*
	 *	Push the child, and yield for a later return.
	 */
	unlang_push(stack, g->children, frame->result, UNLANG_NEXT_SIBLING, UNLANG_SUB_FRAME);
	frame->repeat = true;

	return UNLANG_ACTION_PUSHED_CHILD;
}