Пример #1
0
/** Evaluate a fr_cond_t;
 *
 * @param[in] request the REQUEST
 * @param[in] modreturn the previous module return code
 * @param[in] depth of the recursion (only used for debugging)
 * @param[in] c the condition to evaluate
 * @return -1 on failure, -2 on attribute not found, 0 for "no match", 1 for "match".
 */
int radius_evaluate_cond(REQUEST *request, int modreturn, int depth,
			 fr_cond_t const *c)
{
	int rcode = -1;

	while (c) {
		switch (c->type) {
		case COND_TYPE_EXISTS:
			rcode = radius_evaluate_tmpl(request, modreturn, depth, c->data.vpt);
			/* Existence checks are special, because we expect them to fail */
			if (rcode < 0) rcode = false;
			break;

		case COND_TYPE_MAP:
			rcode = radius_evaluate_map(request, modreturn, depth, c);
			break;

		case COND_TYPE_CHILD:
			rcode = radius_evaluate_cond(request, modreturn, depth + 1, c->data.child);
			break;

		case COND_TYPE_TRUE:
			rcode = true;
			break;

		case COND_TYPE_FALSE:
			rcode = false;
			break;
		default:
			EVAL_DEBUG("FAIL %d", __LINE__);
			return -1;
		}

		if (rcode < 0) return rcode;

		if (c->negate) rcode = !rcode;

		if (!c->next) break;

		/*
		 *	FALSE && ... = FALSE
		 */
		if (!rcode && (c->next_op == COND_AND)) return false;

		/*
		 *	TRUE || ... = TRUE
		 */
		if (rcode && (c->next_op == COND_OR)) return true;

		c = c->next;
	}

	if (rcode < 0) {
		EVAL_DEBUG("FAIL %d", __LINE__);
	}
	return rcode;
}
Пример #2
0
static int do_regex(REQUEST *request, value_pair_map_t const *map, bool iflag)
{
	int compare, rcode;
	int cflags = REG_EXTENDED;
	regex_t reg, *preg;
	char *lhs, *rhs;
	regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];

	if (iflag) cflags |= REG_ICASE;

	/*
	 *	Expand and then compile it.
	 */
	if (map->src->type == VPT_TYPE_REGEX) {
		rcode = radius_expand_tmpl(&rhs, request, map->src);
		if (rcode < 0) {
			EVAL_DEBUG("FAIL %d", __LINE__);
			return -1;
		}
		rad_assert(rhs != NULL);

		compare = regcomp(&reg, rhs, cflags);
		if (compare != 0) {
			if (debug_flag) {
				char errbuf[128];

				regerror(compare, &reg, errbuf, sizeof(errbuf));
				EDEBUG("Failed compiling regular expression: %s", errbuf);
			}
			EVAL_DEBUG("FAIL %d", __LINE__);
			return -1;
		}

		preg = &reg;
	} else {
		preg = map->src->vpt_preg;
	}

	rcode = radius_expand_tmpl(&lhs, request, map->dst);
	if (rcode < 0) {
		EVAL_DEBUG("FAIL %d", __LINE__);
		return -1;
	}
	rad_assert(lhs != NULL);

	memset(&rxmatch, 0, sizeof(rxmatch));	/* regexec does not seem to initialise unused elements */
	compare = regexec(preg, lhs, REQUEST_MAX_REGEX + 1, rxmatch, 0);
	rad_regcapture(request, compare, lhs, rxmatch);

	return (compare == 0);
}
Пример #3
0
static int do_regex(REQUEST *request, char const *lhs, char const *rhs, bool iflag)
{
	int compare;
	int cflags = REG_EXTENDED;
	regex_t reg;
	regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];

	if (iflag) cflags |= REG_ICASE;

	/*
	 *	Include substring matches.
	 */
	compare = regcomp(&reg, rhs, cflags);
	if (compare != 0) {
		if (debug_flag) {
			char errbuf[128];

			regerror(compare, &reg, errbuf, sizeof(errbuf));
			EDEBUG("Failed compiling regular expression: %s", errbuf);
		}
		EVAL_DEBUG("FAIL %d", __LINE__);
		return -1;
	}

	memset(&rxmatch, 0, sizeof(rxmatch));	/* regexec does not seem to initialise unused elements */
	compare = regexec(&reg, lhs, REQUEST_MAX_REGEX + 1, rxmatch, 0);
	regfree(&reg);
	rad_regcapture(request, compare, lhs, rxmatch);

	return (compare == 0);
}
Пример #4
0
static int do_regex(REQUEST *request, const char *lhs, const char *rhs, int iflag)
{
	int i, compare;
	int cflags = REG_EXTENDED;
	regex_t reg;
	regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];

	if (iflag) cflags |= REG_ICASE;

	/*
	 *	Include substring matches.
	 */
	compare = regcomp(&reg, rhs, cflags);
	if (compare != 0) {
		if (debug_flag) {
			char errbuf[128];

			regerror(compare, &reg, errbuf, sizeof(errbuf));
			EDEBUG("Failed compiling regular expression: %s", errbuf);
		}
		EVAL_DEBUG("FAIL %d", __LINE__);
		return -1;
	}
	
	compare = regexec(&reg, lhs, REQUEST_MAX_REGEX + 1, rxmatch, 0);
	regfree(&reg);

	/*
	 *	Add new %{0}, %{1}, etc.
	 */
	if (compare == 0) for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
		char *r;
			
		free(request_data_get(request, request,
				      REQUEST_DATA_REGEX | i));
		
		/*
		 *	No %{i}, skip it.
		 *	We MAY have %{2} without %{1}.
		 */
		if (rxmatch[i].rm_so == -1) continue;
		
		/*
		 *	Copy substring into allocated buffer
		 */
		r = rad_malloc(rxmatch[i].rm_eo -rxmatch[i].rm_so + 1);
		memcpy(r, lhs + rxmatch[i].rm_so,
		       rxmatch[i].rm_eo - rxmatch[i].rm_so);
		r[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';

		request_data_add(request, request,
				 REQUEST_DATA_REGEX | i,
				 r, free);
		}
	return (compare == 0);
}
Пример #5
0
static char *radius_expand_tmpl(REQUEST *request, value_pair_tmpl_t const *vpt)
{
	char *buffer = NULL;
	VALUE_PAIR *vp;

	rad_assert(vpt->type != VPT_TYPE_LIST);

	switch (vpt->type) {
	case VPT_TYPE_LITERAL:
		EVAL_DEBUG("TMPL LITERAL");
		buffer = talloc_strdup(request, vpt->name);
		break;

	case VPT_TYPE_EXEC:
		EVAL_DEBUG("TMPL EXEC");
		buffer = talloc_array(request, char, 1024);
		if (radius_exec_program(vpt->name, request, 1,
					buffer, 1024, NULL, NULL, 0) != 0) {
			talloc_free(buffer);
			return NULL;
		}
		break;

	case VPT_TYPE_REGEX:
		EVAL_DEBUG("TMPL REGEX");
		if (strchr(vpt->name, '%') == NULL) {
			buffer = talloc_strdup(request, vpt->name);
			break;
		}
		/* FALL-THROUGH */

	case VPT_TYPE_XLAT:
		EVAL_DEBUG("TMPL XLAT");
		buffer = NULL;
		if (radius_axlat(&buffer, request, vpt->name, NULL, NULL) == 0) {
			return NULL;
		}
		break;

	case VPT_TYPE_ATTR:
		EVAL_DEBUG("TMPL ATTR");
		vp = radius_vpt_get_vp(request, vpt);
		if (!vp) return NULL;
		buffer = vp_aprint(request, vp);
		break;

	case VPT_TYPE_DATA:
		rad_assert(0 == 1);
		/* FALL-THROUGH */

	default:
		buffer = NULL;
		break;
	}

	EVAL_DEBUG("Expand tmpl --> %s", buffer);
	return buffer;
}
Пример #6
0
/** Expand the RHS of a template
 *
 * @note Length of expanded string can be found with talloc_array_length(*out) - 1
 *
 * @param out where to write a pointer to the newly allocated buffer.
 * @param request Current request.
 * @param vpt to evaluate.
 * @return -1 on error, else 0.
 */
static int radius_expand_tmpl(char **out, REQUEST *request, value_pair_tmpl_t const *vpt)
{
	VALUE_PAIR *vp;
	*out = NULL;

	rad_assert(vpt->type != VPT_TYPE_LIST);

	switch (vpt->type) {
	case VPT_TYPE_LITERAL:
		EVAL_DEBUG("TMPL LITERAL");
		*out = talloc_strdup(request, vpt->name);
		break;

	case VPT_TYPE_EXEC:
		EVAL_DEBUG("TMPL EXEC");
		*out = talloc_array(request, char, 1024);
		if (radius_exec_program(request, vpt->name, true, false, *out, 1024, EXEC_TIMEOUT, NULL, NULL) != 0) {
			TALLOC_FREE(*out);
			return -1;
		}
		break;

	case VPT_TYPE_REGEX:
		EVAL_DEBUG("TMPL REGEX");
		if (strchr(vpt->name, '%') == NULL) {
			*out = talloc_strdup(request, vpt->name);
			break;
		}
		/* FALL-THROUGH */

	case VPT_TYPE_XLAT:
		EVAL_DEBUG("TMPL XLAT");
		/* Error in expansion, this is distinct from zero length expansion */
		if (radius_axlat(out, request, vpt->name, NULL, NULL) < 0) {
			rad_assert(!*out);
			return -1;
		}
		break;

	case VPT_TYPE_ATTR:
		EVAL_DEBUG("TMPL ATTR");
		if (radius_vpt_get_vp(&vp, request, vpt) < 0) {
			return -2;
		}
		*out = vp_aprint(request, vp);
		if (!*out) {
			return -1;
		}
		break;

	case VPT_TYPE_DATA:
		rad_assert(0 == 1);
		/* FALL-THROUGH */

	default:
		break;
	}

	EVAL_DEBUG("Expand tmpl --> %s", *out);
	return 0;
}
Пример #7
0
/** Evaluate a map
 *
 * @param[in] request the REQUEST
 * @param[in] modreturn the previous module return code
 * @param[in] depth of the recursion (only used for debugging)
 * @param[in] c the condition to evaluate
 * @return -1 on error, 0 for "no match", 1 for "match".
 */
int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth,
			fr_cond_t const *c)
{
	int rcode;
	char *lhs, *rhs;
	value_pair_map_t *map;

	rad_assert(c->type == COND_TYPE_MAP);
	map = c->data.map;

	rad_assert(map->dst->type != VPT_TYPE_UNKNOWN);
	rad_assert(map->src->type != VPT_TYPE_UNKNOWN);
	rad_assert(map->dst->type != VPT_TYPE_LIST);
	rad_assert(map->src->type != VPT_TYPE_LIST);
	rad_assert(map->dst->type != VPT_TYPE_REGEX);

	/*
	 *	Verify regexes.
	 */
	if (map->src->type == VPT_TYPE_REGEX) {
		rad_assert(map->op == T_OP_REG_EQ);
	} else {
		rad_assert(!((map->op == T_OP_REG_EQ) || (map->op == T_OP_REG_NE)));
	}

	/*
	 *	They're both attributes.  Do attribute-specific work.
	 *
	 *	LHS is DST.  RHS is SRC <sigh>
	 */
	if (!c->cast && (map->src->type == VPT_TYPE_ATTR) && (map->dst->type == VPT_TYPE_ATTR)) {
		VALUE_PAIR *lhs_vp, *rhs_vp;

		EVAL_DEBUG("ATTR to ATTR");
		if ((radius_vpt_get_vp(&lhs_vp, request, map->dst) < 0) ||
		    (radius_vpt_get_vp(&rhs_vp, request, map->src) < 0)) {
		    return -2;
		}

		return paircmp_op(lhs_vp, map->op, rhs_vp);
	}

	/*
	 *	LHS is a cast.  Do type-specific comparisons, as if
	 *	the LHS was a real attribute.
	 */
	if (c->cast) {
		VALUE_PAIR *lhs_vp, *rhs_vp;

		rcode = get_cast_vp(&lhs_vp, request, map->dst, c->cast);
		if (rcode < 0) {
			return rcode;
		}
		rad_assert(lhs_vp);

		/*
		 *	Get either a real VP, or parse the RHS into a
		 *	VP, and return that.
		 */
		if (map->src->type == VPT_TYPE_ATTR) {
			if (radius_vpt_get_vp(&rhs_vp, request, map->src) < 0) {
				return -2;
			}
		} else {
			rcode = get_cast_vp(&rhs_vp, request, map->src, c->cast);
			if (rcode < 0) {
				return rcode;
			}
			rad_assert(rhs_vp);
		}
		if (!rhs_vp) return -2;

		EVAL_DEBUG("CAST to ...");

		rcode = paircmp_op(lhs_vp, map->op, rhs_vp);
		pairfree(&lhs_vp);
		if (map->src->type != VPT_TYPE_ATTR) {
			pairfree(&rhs_vp);
		}
		return rcode;
	}

	/*
	 *	Might be a virtual comparison
	 */
	if ((map->dst->type == VPT_TYPE_ATTR) &&
	    (map->src->type != VPT_TYPE_REGEX) &&
	    (c->pass2_fixup == PASS2_PAIRCOMPARE)) {
		VALUE_PAIR *rhs_vp;

		EVAL_DEBUG("virtual ATTR to DATA");

		rcode = get_cast_vp(&rhs_vp, request, map->src, map->dst->da);
		if (rcode < 0) {
			return rcode;
		}
		rad_assert(rhs_vp);

		if (paircompare(request, request->packet->vps, rhs_vp, NULL) == 0) {
			return true;
		}
		return false;
	}
	rad_assert(c->pass2_fixup != PASS2_PAIRCOMPARE);

	/*
	 *	RHS has been pre-parsed into binary data.  Go check
	 *	that.
	 */
	if ((map->dst->type == VPT_TYPE_ATTR) &&
	    (map->src->type == VPT_TYPE_DATA)) {
		VALUE_PAIR *lhs_vp, *rhs_vp;

		EVAL_DEBUG("ATTR to DATA");

		if (radius_vpt_get_vp(&lhs_vp, request, map->dst) < 0) {
			return -2;
		}

		rcode = get_cast_vp(&rhs_vp, request, map->src, map->dst->da);
		if (rcode < 0) {
			return rcode;
		}
		rad_assert(rhs_vp);

#ifdef WITH_EVAL_DEBUG
		debug_pair(lhs_vp);
		debug_pair(rhs_vp);
#endif

		rcode = paircmp_op(lhs_vp, map->op, rhs_vp);
		pairfree(&rhs_vp);
		return rcode;
	}

	rad_assert(map->src->type != VPT_TYPE_DATA);
	rad_assert(map->dst->type != VPT_TYPE_DATA);

	/*
	 *	The RHS now needs to be expanded into a string.
	 */
	rcode = radius_expand_tmpl(&rhs, request, map->src);
	if (rcode < 0) {
		EVAL_DEBUG("FAIL %d", __LINE__);
		return rcode;
	}

	/*
	 *	User-Name == FOO
	 *
	 *	Parse the RHS to be the same DA as the LHS.  do
	 *	comparisons.  So long as it's not a regex, which does
	 *	string comparisons.
	 *
	 *	The LHS may be a virtual attribute, too.
	 */
	if ((map->dst->type == VPT_TYPE_ATTR) &&
	    (map->src->type != VPT_TYPE_REGEX)) {
		VALUE_PAIR *lhs_vp, *rhs_vp;

		EVAL_DEBUG("ATTR to non-REGEX");

		/*
		 *	No LHS means no match
		 */
		if (radius_vpt_get_vp(&lhs_vp, request, map->dst) < 0) {
			/*
			 *	Not a real attr: might be a dynamic comparison.
			 */
			if ((map->dst->type == VPT_TYPE_ATTR) &&
			    (map->dst->da->vendor == 0) &&
			    radius_find_compare(map->dst->da)) {
				rhs_vp = pairalloc(request, map->dst->da);

				if (!pairparsevalue(rhs_vp, rhs)) {
					talloc_free(rhs);
					EVAL_DEBUG("FAIL %d", __LINE__);
					return -1;
				}
				talloc_free(rhs);

				rcode = (radius_callback_compare(request, NULL, rhs_vp, NULL, NULL) == 0);
				pairfree(&rhs_vp);
				return rcode;
			}

			return -2;
		}

		/*
		 *	Get VP for RHS
		 */
		rhs_vp = pairalloc(request, map->dst->da);
		rad_assert(rhs_vp != NULL);
		if (!pairparsevalue(rhs_vp, rhs)) {
			talloc_free(rhs);
			pairfree(&rhs_vp);
			EVAL_DEBUG("FAIL %d", __LINE__);
			return -1;
		}

		rcode = paircmp_op(lhs_vp, map->op, rhs_vp);
		talloc_free(rhs);
		pairfree(&rhs_vp);
		return rcode;
	}

	/*
	 *	The LHS is a string.  Expand it.
	 */
	rcode = radius_expand_tmpl(&lhs, request, map->dst);
	if (rcode < 0) {
		EVAL_DEBUG("FAIL %d", __LINE__);
		return rcode;
	}

	EVAL_DEBUG("LHS is %s", lhs);

	/*
	 *	Compile  the RHS to a regex, and do regex stuff
	 */
	if (map->src->type == VPT_TYPE_REGEX) {
		return do_regex(request, lhs, rhs, c->regex_i);
	}

	/*
	 *	Loop over the string, doing comparisons
	 */
	if (all_digits(lhs) && all_digits(rhs)) {
		int lint, rint;

		lint = strtoul(lhs, NULL, 0);
		rint = strtoul(rhs, NULL, 0);
		talloc_free(lhs);
		talloc_free(rhs);

		switch (map->op) {
		case T_OP_CMP_EQ:
			return (lint == rint);

		case T_OP_NE:
			return (lint != rint);

		case T_OP_LT:
			return (lint < rint);

		case T_OP_GT:
			return (lint > rint);

		case T_OP_LE:
			return (lint <= rint);

		case T_OP_GE:
			return (lint >= rint);

		default:
			break;
		}

	} else {
		rcode = strcmp(lhs, rhs);
		talloc_free(lhs);
		talloc_free(rhs);

		switch (map->op) {
		case T_OP_CMP_EQ:
			return (rcode == 0);

		case T_OP_NE:
			return (rcode != 0);

		case T_OP_LT:
			return (rcode < 0);

		case T_OP_GT:
			return (rcode > 0);

		case T_OP_LE:
			return (rcode <= 0);

		case T_OP_GE:
			return (rcode >= 0);

		default:
			break;
		}
	}

	EVAL_DEBUG("FAIL %d", __LINE__);
	return -1;
}
Пример #8
0
/** Evaluate a template
 *
 * @param[in] request the REQUEST
 * @param[in] modreturn the previous module return code
 * @param[in] depth of the recursion (only used for debugging)
 * @param[in] vpt the template to evaluate
 * @return -1 on error, 0 for "no match", 1 for "match".
 */
int radius_evaluate_tmpl(REQUEST *request, int modreturn, UNUSED int depth,
			 value_pair_tmpl_t const *vpt)
{
	int rcode;
	int modcode;
	char *buffer;

	switch (vpt->type) {
	case VPT_TYPE_LITERAL:
		modcode = fr_str2int(modreturn_table, vpt->name, RLM_MODULE_UNKNOWN);
		if (modcode != RLM_MODULE_UNKNOWN) {
			rcode = (modcode == modreturn);
			break;
		}

		/*
		 *	Else it's a literal string.  Empty string is
		 *	false, non-empty string is true.
		 *
		 *	@todo: Maybe also check for digits?
		 *
		 *	The VPT *doesn't* have a "bare word" type,
		 *	which arguably it should.
		 */
		rcode = (vpt->name != '\0');
		break;

	case VPT_TYPE_ATTR:
	case VPT_TYPE_LIST:
		if (radius_vpt_get_vp(NULL, request, vpt) == 0) {
			rcode = true;
		} else {
			rcode = false;
		}
		break;

		/*
		 *	FIXME: expand the strings
		 *	if not empty, return!
		 */
	case VPT_TYPE_XLAT:
	case VPT_TYPE_EXEC:
		if (!*vpt->name) return false;
		rcode = radius_expand_tmpl(&buffer, request, vpt);
		if (rcode < 0) {
			EVAL_DEBUG("FAIL %d", __LINE__);
			return -1;
		}
		rcode = (buffer && (*buffer != '\0'));
		talloc_free(buffer);
		break;

		/*
		 *	Can't have a bare ... (/foo/) ...
		 */
	case VPT_TYPE_REGEX:
		EVAL_DEBUG("FAIL %d", __LINE__);
		rad_assert(0 == 1);
		/* FALL-THROUGH */

	default:
		rcode = -1;
		break;
	}

	return rcode;
}
Пример #9
0
/*
 *	Copy data from src to dst, where the attributes are of
 *	different type.
 */
static bool do_cast_copy(VALUE_PAIR *dst, VALUE_PAIR const *src)
{
	rad_assert(dst->da->type != src->da->type);

	if (dst->da->type == PW_TYPE_STRING) {
		dst->vp_strvalue = vp_aprint(dst, src);
		dst->length = strlen(dst->vp_strvalue);
		return true;
	}

	if (dst->da->type == PW_TYPE_OCTETS) {
		if (src->da->type == PW_TYPE_STRING) {
			pairmemcpy(dst, src->vp_octets, src->length);	/* Copy embedded NULLs */
		} else {
			pairmemcpy(dst, (uint8_t const *) &src->data, src->length);
		}
		return true;
	}

	if (src->da->type == PW_TYPE_STRING) {
		return pairparsevalue(dst, src->vp_strvalue);
	}

	if ((src->da->type == PW_TYPE_INTEGER64) &&
	    (dst->da->type == PW_TYPE_ETHERNET)) {
		uint8_t array[8];
		uint64_t i;

		i = htonll(src->vp_integer64);
		memcpy(array, &i, 8);

		/*
		 *	For OUIs in the DB.
		 */
		if ((array[0] != 0) || (array[1] != 0)) return false;

		memcpy(&dst->vp_ether, &array[2], 6);
		dst->length = 6;
		return true;
	}

	/*
	 *	The attribute we've found has to have a size which is
	 *	compatible with the type of the destination cast.
	 */
	if ((src->length < dict_attr_sizes[dst->da->type][0]) ||
	    (src->length > dict_attr_sizes[dst->da->type][1])) {
		EVAL_DEBUG("Casted attribute is wrong size (%u)", (unsigned int) src->length);
		return false;
	}

	if (src->da->type == PW_TYPE_OCTETS) {
		switch (dst->da->type) {
		case PW_TYPE_INTEGER64:
			dst->vp_integer = ntohll(*(uint64_t const *) src->vp_octets);
			break;


		case PW_TYPE_INTEGER:
		case PW_TYPE_DATE:
		case PW_TYPE_SIGNED:
			dst->vp_integer = ntohl(*(uint32_t const *) src->vp_octets);
			break;

		case PW_TYPE_SHORT:
			dst->vp_integer = ntohs(*(uint16_t const *) src->vp_octets);
			break;

		case PW_TYPE_BYTE:
			dst->vp_integer = src->vp_octets[0];
			break;

		default:
			memcpy(&dst->data, src->vp_octets, src->length);
			break;
		}

		dst->length = src->length;
		return true;
	}

	/*
	 *	Convert host order to network byte order.
	 */
	if ((dst->da->type == PW_TYPE_IPADDR) &&
	    ((src->da->type == PW_TYPE_INTEGER) ||
	     (src->da->type == PW_TYPE_DATE) ||
	     (src->da->type == PW_TYPE_SIGNED))) {
		dst->vp_ipaddr = htonl(src->vp_integer);

	} else if ((src->da->type == PW_TYPE_IPADDR) &&
		   ((dst->da->type == PW_TYPE_INTEGER) ||
		    (dst->da->type == PW_TYPE_DATE) ||
		    (dst->da->type == PW_TYPE_SIGNED))) {
		dst->vp_integer = htonl(src->vp_ipaddr);

	} else {		/* they're of the same byte order */
		memcpy(&dst->data, &src->data, src->length);
	}

	dst->length = src->length;

	return true;
}
Пример #10
0
/** Expand the RHS of a template
 *
 * @note Length of expanded string can be found with talloc_array_length(*out) - 1
 *
 * @param out where to write a pointer to the newly allocated buffer.
 * @param request Current request.
 * @param vpt to evaluate.
 * @return -1 on error, else 0.
 */
int radius_expand_tmpl(char **out, REQUEST *request, value_pair_tmpl_t const *vpt)
{
	VALUE_PAIR *vp;
	*out = NULL;

	rad_assert(vpt->type != TMPL_TYPE_LIST);

	switch (vpt->type) {
	case TMPL_TYPE_LITERAL:
		EVAL_DEBUG("TMPL LITERAL");
		*out = talloc_typed_strdup(request, vpt->name);
		break;

	case TMPL_TYPE_EXEC:
		EVAL_DEBUG("TMPL EXEC");
		*out = talloc_array(request, char, 1024);
		if (radius_exec_program(request, vpt->name, true, false, *out, 1024, EXEC_TIMEOUT, NULL, NULL) != 0) {
			TALLOC_FREE(*out);
			return -1;
		}
		break;

	case TMPL_TYPE_REGEX:
		EVAL_DEBUG("TMPL REGEX");
		/* Error in expansion, this is distinct from zero length expansion */
		if (radius_axlat(out, request, vpt->name, NULL, NULL) < 0) {
			rad_assert(!*out);
			return -1;
		}
		break;

	case TMPL_TYPE_XLAT:
		EVAL_DEBUG("TMPL XLAT");
		/* Error in expansion, this is distinct from zero length expansion */
		if (radius_axlat(out, request, vpt->name, NULL, NULL) < 0) {
			rad_assert(!*out);
			return -1;
		}
		break;

	case TMPL_TYPE_XLAT_STRUCT:
		EVAL_DEBUG("TMPL XLAT_STRUCT");
		/* Error in expansion, this is distinct from zero length expansion */
		if (radius_axlat_struct(out, request, vpt->tmpl_xlat, NULL, NULL) < 0) {
			rad_assert(!*out);
			return -1;
		}
		RDEBUG2("EXPAND %s", vpt->name); /* xlat_struct doesn't do this */
		RDEBUG2("   --> %s", *out);
		break;

	case TMPL_TYPE_ATTR:
	{
		int ret;

		EVAL_DEBUG("TMPL ATTR");
		ret = tmpl_find_vp(&vp, request, vpt);
		if (ret < 0) return -2;

		*out = vp_aprint_value(request, vp, false);
		if (!*out) return -1;
	}
		break;

	case TMPL_TYPE_DATA:
	case TMPL_TYPE_REGEX_STRUCT:
		rad_assert(0 == 1);
		/* FALL-THROUGH */

	default:
		break;
	}

	EVAL_DEBUG("Expand tmpl --> %s", *out);
	return 0;
}
Пример #11
0
/** Evaluate a map
 *
 * @param[in] request the REQUEST
 * @param[in] modreturn the previous module return code
 * @param[in] depth of the recursion (only used for debugging)
 * @param[in] c the condition to evaluate
 * @return -1 on error, 0 for "no match", 1 for "match".
 */
int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth,
			fr_cond_t const *c)
{
	int rcode;
	char *lhs, *rhs;
	value_pair_map_t *map;

	rad_assert(c->type == COND_TYPE_MAP);
	map = c->data.map;

	rad_assert(map->dst->type != TMPL_TYPE_UNKNOWN);
	rad_assert(map->src->type != TMPL_TYPE_UNKNOWN);
	rad_assert(map->dst->type != TMPL_TYPE_LIST);
	rad_assert(map->src->type != TMPL_TYPE_LIST);
	rad_assert(map->dst->type != TMPL_TYPE_REGEX);
	rad_assert(map->dst->type != TMPL_TYPE_REGEX_STRUCT);

	EVAL_DEBUG("MAP TYPES LHS: %s, RHS: %s",
		   fr_int2str(template_names, map->dst->type, "???"),
		   fr_int2str(template_names, map->src->type, "???"));

	/*
	 *	Verify regexes.
	 */
	if ((map->src->type == TMPL_TYPE_REGEX) ||
	    (map->src->type == TMPL_TYPE_REGEX_STRUCT)) {
		rad_assert(map->op == T_OP_REG_EQ);
	} else {
		rad_assert(!((map->op == T_OP_REG_EQ) || (map->op == T_OP_REG_NE)));
	}

	/*
	 *	They're both attributes.  Do attribute-specific work.
	 *
	 *	LHS is DST.  RHS is SRC <sigh>
	 */
	if (!c->cast && (map->src->type == TMPL_TYPE_ATTR) && (map->dst->type == TMPL_TYPE_ATTR)) {
		VALUE_PAIR *lhs_vp, *rhs_vp, *cast_vp;

		EVAL_DEBUG("ATTR to ATTR");

		if ((tmpl_find_vp(&lhs_vp, request, map->dst) < 0) ||
		    (tmpl_find_vp(&rhs_vp, request, map->src) < 0)) return -1;

		if (map->dst->tmpl_da->type == map->src->tmpl_da->type) {
			return paircmp_op(lhs_vp, map->op, rhs_vp);
		}

		/*
		 *	Compare a large integer (lhs) to a small integer (rhs).
		 *	We allow this without a cast.
		 */
		rad_assert((map->dst->tmpl_da->type == PW_TYPE_INTEGER64) ||
			   (map->dst->tmpl_da->type == PW_TYPE_INTEGER) ||
			   (map->dst->tmpl_da->type == PW_TYPE_SHORT));
		rad_assert((map->src->tmpl_da->type == PW_TYPE_INTEGER) ||
			   (map->src->tmpl_da->type == PW_TYPE_SHORT) ||
			   (map->src->tmpl_da->type == PW_TYPE_BYTE));

		cast_vp = pairalloc(request, lhs_vp->da);
		if (!cast_vp) return false;

		/*
		 *	Copy the RHS to the casted type.
		 */
		if (do_cast_copy(cast_vp, rhs_vp) < 0) {
			talloc_free(cast_vp);
			return false;
		}

		rcode = paircmp_op(lhs_vp, map->op, cast_vp);
		talloc_free(cast_vp);
		return rcode;
	}

	/*
	 *	LHS is a cast.  Do type-specific comparisons, as if
	 *	the LHS was a real attribute.
	 */
	if (c->cast) {
		VALUE_PAIR *lhs_vp, *rhs_vp;

		/*
		 *	Try to copy data from the VP which is being
		 *	casted, instead of printing it to a string and
		 *	then re-parsing it.
		 */
		if (map->dst->type == TMPL_TYPE_ATTR) {
			VALUE_PAIR *cast_vp;

			if (tmpl_find_vp(&cast_vp, request, map->dst) < 0) return false;

			lhs_vp = pairalloc(request, c->cast);
			if (!lhs_vp) return -1;

			/*
			 *	In a separate function for clarity
			 */
			if (do_cast_copy(lhs_vp, cast_vp) < 0) {
				talloc_free(lhs_vp);
				return -1;
			}

		} else {
			rcode = tmpl_cast_to_vp(&lhs_vp, request, map->dst, c->cast);
			if (rcode < 0) {
				return rcode;
			}
		}
		rad_assert(lhs_vp);

		/*
		 *	Get either a real VP, or parse the RHS into a
		 *	VP, and return that.
		 */
		if (map->src->type == TMPL_TYPE_ATTR) {
			if (tmpl_find_vp(&rhs_vp, request, map->src) < 0) {
				return -2;
			}
		} else {
			rcode = tmpl_cast_to_vp(&rhs_vp, request, map->src, c->cast);
			if (rcode < 0) {
				return rcode;
			}
			rad_assert(rhs_vp);
		}
		if (!rhs_vp) return -2;

		EVAL_DEBUG("CAST to %s",
			   fr_int2str(dict_attr_types,
				      c->cast->type, "?Unknown?"));

		rcode = paircmp_op(lhs_vp, map->op, rhs_vp);
		pairfree(&lhs_vp);
		if (map->src->type != TMPL_TYPE_ATTR) {
			pairfree(&rhs_vp);
		}
		return rcode;
	}

	/*
	 *	Might be a virtual comparison
	 */
	if ((map->dst->type == TMPL_TYPE_ATTR) &&
	    (map->src->type != TMPL_TYPE_REGEX) &&
	    (map->src->type != TMPL_TYPE_REGEX_STRUCT) &&
	    (c->pass2_fixup == PASS2_PAIRCOMPARE)) {
		int ret;
		VALUE_PAIR *lhs_vp;

		EVAL_DEBUG("virtual ATTR to DATA");

		rcode = tmpl_cast_to_vp(&lhs_vp, request, map->src, map->dst->tmpl_da);
		if (rcode < 0) return rcode;
		rad_assert(lhs_vp);

		/*
		 *	paircompare requires the operator be set for the
		 *	check attribute.
		 */
		lhs_vp->op = map->op;
		ret = paircompare(request, request->packet->vps, lhs_vp, NULL);
		talloc_free(lhs_vp);
		if (ret == 0) {
			return true;
		}
		return false;
	}
	rad_assert(c->pass2_fixup != PASS2_PAIRCOMPARE);

	/*
	 *	RHS has been pre-parsed into binary data.  Go check
	 *	that.
	 */
	if ((map->dst->type == TMPL_TYPE_ATTR) &&
	    (map->src->type == TMPL_TYPE_DATA)) {
		VALUE_PAIR *lhs_vp, *rhs_vp;

		EVAL_DEBUG("ATTR to DATA");

		if (tmpl_find_vp(&lhs_vp, request, map->dst) < 0) return -2;

		rcode = tmpl_cast_to_vp(&rhs_vp, request, map->src, map->dst->tmpl_da);
		if (rcode < 0) return rcode;
		rad_assert(rhs_vp);

#ifdef WITH_EVAL_DEBUG
		debug_pair(lhs_vp);
		debug_pair(rhs_vp);
#endif

		rcode = paircmp_op(lhs_vp, map->op, rhs_vp);
		pairfree(&rhs_vp);
		return rcode;
	}

	rad_assert(map->src->type != TMPL_TYPE_DATA);
	rad_assert(map->dst->type != TMPL_TYPE_DATA);

#ifdef HAVE_REGEX_H
	/*
	 *	Parse regular expressions.
	 */
	if ((map->src->type == TMPL_TYPE_REGEX) ||
	    (map->src->type == TMPL_TYPE_REGEX_STRUCT)) {
		return do_regex(request, map);
	}
#endif

	/*
	 *	The RHS now needs to be expanded into a string.
	 */
	rcode = radius_expand_tmpl(&rhs, request, map->src);
	if (rcode < 0) {
		EVAL_DEBUG("FAIL %d", __LINE__);
		return rcode;
	}
	rad_assert(rhs != NULL);

	/*
	 *	User-Name == FOO
	 *
	 *	Parse the RHS to be the same DA as the LHS.  do
	 *	comparisons.  So long as it's not a regex, which does
	 *	string comparisons.
	 *
	 *	The LHS may be a virtual attribute, too.
	 */
	if (map->dst->type == TMPL_TYPE_ATTR) {
		VALUE_PAIR *lhs_vp, *rhs_vp;

		EVAL_DEBUG("ATTR to non-REGEX");

		/*
		 *	No LHS means no match
		 */
		if (tmpl_find_vp(&lhs_vp, request, map->dst) < 0) {
			/*
			 *	Not a real attr: might be a dynamic comparison.
			 */
			if ((map->dst->type == TMPL_TYPE_ATTR) &&
			    (map->dst->tmpl_da->vendor == 0) &&
			    radius_find_compare(map->dst->tmpl_da)) {
				rhs_vp = pairalloc(request, map->dst->tmpl_da);
				rad_assert(rhs_vp != NULL);
				if (pairparsevalue(rhs_vp, rhs, 0) < 0) {
					talloc_free(rhs);
					EVAL_DEBUG("FAIL %d", __LINE__);
					return -1;
				}
				talloc_free(rhs);

				rcode = (radius_callback_compare(request, NULL, rhs_vp, NULL, NULL) == 0);
				pairfree(&rhs_vp);
				return rcode;
			}

			return -2;
		}

		/*
		 *	Get VP for RHS
		 */
		rhs_vp = pairalloc(request, map->dst->tmpl_da);
		rad_assert(rhs_vp != NULL);
		if (pairparsevalue(rhs_vp, rhs, 0) < 0) {
			talloc_free(rhs);
			pairfree(&rhs_vp);
			EVAL_DEBUG("FAIL %d", __LINE__);
			return -1;
		}

		rcode = paircmp_op(lhs_vp, map->op, rhs_vp);
		talloc_free(rhs);
		pairfree(&rhs_vp);
		return rcode;
	}

	/*
	 *	The LHS is a string.  Expand it.
	 */
	rcode = radius_expand_tmpl(&lhs, request, map->dst);
	if (rcode < 0) {
		EVAL_DEBUG("FAIL %d", __LINE__);
		return rcode;
	}
	rad_assert(lhs != NULL);

	EVAL_DEBUG("LHS is %s", lhs);

	/*
	 *	Loop over the string, doing comparisons
	 */
	if (all_digits(lhs) && all_digits(rhs)) {
		int lint, rint;

		lint = strtoul(lhs, NULL, 0);
		rint = strtoul(rhs, NULL, 0);
		talloc_free(lhs);
		talloc_free(rhs);

		switch (map->op) {
		case T_OP_CMP_EQ:
			return (lint == rint);

		case T_OP_NE:
			return (lint != rint);

		case T_OP_LT:
			return (lint < rint);

		case T_OP_GT:
			return (lint > rint);

		case T_OP_LE:
			return (lint <= rint);

		case T_OP_GE:
			return (lint >= rint);

		default:
			break;
		}

	} else {
		rad_assert(lhs != NULL);
		rad_assert(rhs != NULL);

		rcode = strcmp(lhs, rhs);
		talloc_free(lhs);
		talloc_free(rhs);

		switch (map->op) {
		case T_OP_CMP_EQ:
			return (rcode == 0);

		case T_OP_NE:
			return (rcode != 0);

		case T_OP_LT:
			return (rcode < 0);

		case T_OP_GT:
			return (rcode > 0);

		case T_OP_LE:
			return (rcode <= 0);

		case T_OP_GE:
			return (rcode >= 0);

		default:
			break;
		}
	}

	EVAL_DEBUG("FAIL %d", __LINE__);
	return -1;
}
Пример #12
0
/*
 *	Copy data from src to dst, where the attributes are of
 *	different type.
 */
static int do_cast_copy(VALUE_PAIR *dst, VALUE_PAIR const *src)
{
	rad_assert(dst->da->type != src->da->type);

	if (dst->da->type == PW_TYPE_STRING) {
		dst->vp_strvalue = vp_aprint_value(dst, src, false);
		dst->length = strlen(dst->vp_strvalue);
		return 0;
	}

	if (dst->da->type == PW_TYPE_OCTETS) {
		if (src->da->type == PW_TYPE_STRING) {
			pairmemcpy(dst, src->vp_octets, src->length);	/* Copy embedded NULLs */
		} else {
			pairmemcpy(dst, (uint8_t const *) &src->data, src->length);
		}
		return 0;
	}

	if (src->da->type == PW_TYPE_STRING) {
		return pairparsevalue(dst, src->vp_strvalue, 0);
	}

	if ((src->da->type == PW_TYPE_INTEGER64) &&
	    (dst->da->type == PW_TYPE_ETHERNET)) {
		uint8_t array[8];
		uint64_t i;

		i = htonll(src->vp_integer64);
		memcpy(array, &i, 8);

		/*
		 *	For OUIs in the DB.
		 */
		if ((array[0] != 0) || (array[1] != 0)) return -1;

		memcpy(&dst->vp_ether, &array[2], 6);
		dst->length = 6;
		return 0;
	}

	/*
	 *	For integers, we allow the casting of a SMALL type to
	 *	a larger type, but not vice-versa.
	 */
	if (dst->da->type == PW_TYPE_INTEGER64) {
		switch (src->da->type) {
		case PW_TYPE_BYTE:
			dst->vp_integer64 = src->vp_byte;
			break;

		case PW_TYPE_SHORT:
			dst->vp_integer64 = src->vp_short;
			break;

		case PW_TYPE_INTEGER:
			dst->vp_integer64 = src->vp_integer;
			break;

		case PW_TYPE_OCTETS:
			goto do_octets;

		default:
			EVAL_DEBUG("Invalid cast to integer64");
			return -1;

		}
		return 0;
	}

	/*
	 *	We can compare LONG integers to SHORTER ones, so long
	 *	as the long one is on the LHS.
	 */
	if (dst->da->type == PW_TYPE_INTEGER) {
		switch (src->da->type) {
		case PW_TYPE_BYTE:
			dst->vp_integer = src->vp_byte;
			break;

		case PW_TYPE_SHORT:
			dst->vp_integer = src->vp_short;
			break;

		case PW_TYPE_OCTETS:
			goto do_octets;

		default:
			EVAL_DEBUG("Invalid cast to integer");
			return -1;

		}
		return 0;
	}

	if (dst->da->type == PW_TYPE_SHORT) {
		switch (src->da->type) {
		case PW_TYPE_BYTE:
			dst->vp_short = src->vp_byte;
			break;

		case PW_TYPE_OCTETS:
			goto do_octets;

		default:
			EVAL_DEBUG("Invalid cast to short");
			return -1;

		}
		return 0;
	}

	/*
	 *	The attribute we've found has to have a size which is
	 *	compatible with the type of the destination cast.
	 */
	if ((src->length < dict_attr_sizes[dst->da->type][0]) ||
	    (src->length > dict_attr_sizes[dst->da->type][1])) {
		EVAL_DEBUG("Casted attribute is wrong size (%u)", (unsigned int) src->length);
		return -1;
	}

	if (src->da->type == PW_TYPE_OCTETS) {
	do_octets:
		switch (dst->da->type) {
		case PW_TYPE_INTEGER64:
			dst->vp_integer = ntohll(*(uint64_t const *) src->vp_octets);
			break;

		case PW_TYPE_INTEGER:
		case PW_TYPE_DATE:
		case PW_TYPE_SIGNED:
			dst->vp_integer = ntohl(*(uint32_t const *) src->vp_octets);
			break;

		case PW_TYPE_SHORT:
			dst->vp_integer = ntohs(*(uint16_t const *) src->vp_octets);
			break;

		case PW_TYPE_BYTE:
			dst->vp_integer = src->vp_octets[0];
			break;

		default:
			memcpy(&dst->data, src->vp_octets, src->length);
			break;
		}

		dst->length = src->length;
		return 0;
	}

	/*
	 *	Convert host order to network byte order.
	 */
	if ((dst->da->type == PW_TYPE_IPV4_ADDR) &&
	    ((src->da->type == PW_TYPE_INTEGER) ||
	     (src->da->type == PW_TYPE_DATE) ||
	     (src->da->type == PW_TYPE_SIGNED))) {
		dst->vp_ipaddr = htonl(src->vp_integer);

	} else if ((src->da->type == PW_TYPE_IPV4_ADDR) &&
		   ((dst->da->type == PW_TYPE_INTEGER) ||
		    (dst->da->type == PW_TYPE_DATE) ||
		    (dst->da->type == PW_TYPE_SIGNED))) {
		dst->vp_integer = htonl(src->vp_ipaddr);

	} else {		/* they're of the same byte order */
		memcpy(&dst->data, &src->data, src->length);
	}

	dst->length = src->length;

	return 0;
}
Пример #13
0
static int do_regex(REQUEST *request, value_pair_map_t const *map)
{
	int compare, rcode, ret;
	regex_t reg, *preg;
	char *lhs, *rhs;
	regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];

	/*
	 *  Expand and then compile it.
	 */
	switch (map->src->type) {
	case TMPL_TYPE_REGEX:
		rcode = radius_expand_tmpl(&rhs, request, map->src);
		if (rcode < 0) {
			EVAL_DEBUG("FAIL %d", __LINE__);
			return -1;
		}
		rad_assert(rhs != NULL);

		compare = regcomp(&reg, rhs, REG_EXTENDED | (map->src->tmpl_iflag ? REG_ICASE : 0));
		if (compare != 0) {
			if (debug_flag) {
				char errbuf[128];

				regerror(compare, &reg, errbuf, sizeof(errbuf));
				ERROR("Failed compiling regular expression: %s", errbuf);
			}
			EVAL_DEBUG("FAIL %d", __LINE__);
			return -1;
		}

		preg = &reg;
		break;

	case TMPL_TYPE_REGEX_STRUCT:
		preg = map->src->tmpl_preg;
		break;

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

	rcode = radius_expand_tmpl(&lhs, request, map->dst);
	if (rcode < 0) {
		EVAL_DEBUG("FAIL %d", __LINE__);
		ret = -1;
		goto finish;
	}
	rad_assert(lhs != NULL);

	/*
	 *  regexec doesn't initialise unused elements
	 */
	memset(&rxmatch, 0, sizeof(rxmatch));
	compare = regexec(preg, lhs, REQUEST_MAX_REGEX + 1, rxmatch, 0);
	rad_regcapture(request, compare, lhs, rxmatch);
	ret = (compare == 0);

finish:
	/*
	 *  regcomp allocs extra memory for the expression, so if the
	 *  result wasn't cached we need to free it here.
	 */
	if (preg == &reg) regfree(&reg);

	return ret;
}