Esempio n. 1
0
static int arp_socket_decode(UNUSED rad_listen_t *listener, UNUSED REQUEST *request)
{
	int i;
	arp_over_ether_t const *arp;
	uint8_t const *p;

	arp = (arp_over_ether_t const *) request->packet->data;
	/*
	 *	arp_socket_recv() takes care of validating it's really
	 *	our kind of ARP.
	 */
	for (i = 0, p = (uint8_t const *) arp;
	     header_names[i].name != NULL;
	     p += header_names[i].len, i++) {
		ssize_t len;
		DICT_ATTR const *da;
		VALUE_PAIR *vp;

		da = dict_attrbyname(header_names[i].name);
		if (!da) return 0;

		vp = NULL;
		len = data2vp(request->packet, NULL, NULL, da, p,
			      header_names[i].len, header_names[i].len,
			      &vp);
		if (len <= 0) {
			RDEBUG("Failed decoding %s: %s",
			       header_names[i].name, fr_strerror());
			return 0;
		}

		debug_pair(vp);
		pairadd(&request->packet->vps, vp);
	}

	return 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;
}
Esempio n. 3
0
int vqp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
{
	int i, code, length;
	VALUE_PAIR *vp;
	uint8_t *ptr;
	VALUE_PAIR	*vps[VQP_MAX_ATTRIBUTES];

	if (!packet) {
		fr_strerror_printf("Failed encoding VQP");
		return -1;
	}

	if (packet->data) return 0;

	vp = pairfind(packet->vps, PW_VQP_PACKET_TYPE);
	if (!vp) {
		fr_strerror_printf("Failed to find VQP-Packet-Type in response packet");
		return -1;
	}

	code = vp->lvalue;
	if ((code < 1) || (code > 4)) {
		fr_strerror_printf("Invalid value %d for VQP-Packet-Type", code);
		return -1;
	}

	length = VQP_HDR_LEN;
	memset(vps, 0, sizeof(vps));

	vp = pairfind(packet->vps, PW_VQP_ERROR_CODE);

	/*
	 *	FIXME: Map attributes from calling-station-Id, etc.
	 *
	 *	Maybe do this via rlm_vqp?  That's probably the
	 *	best place to add the code...
	 */

	/*
	 *	No error: encode attributes.
	 */
	if (!vp) for (i = 0; i < VQP_MAX_ATTRIBUTES; i++) {
		if (!contents[code][i]) break;

		vps[i] = pairfind(packet->vps, contents[code][i] | 0x2000);

		/*
		 *	FIXME: Print the name...
		 */
		if (!vps[i]) {
			fr_strerror_printf("Failed to find VQP attribute %02x",
				   contents[code][i]);
			return -1;
		}

		length += 6;
		length += vps[i]->length;
	}

	packet->data = malloc(length);
	if (!packet->data) {
		fr_strerror_printf("No memory");
		return -1;
	}
	packet->data_len = length;

	ptr = packet->data;

	ptr[0] = VQP_VERSION;
	ptr[1] = code;

	if (!vp) {
		ptr[2] = 0;
	} else {
		ptr[2] = vp->lvalue & 0xff;
		return 0;
	}

	/*
	 *	The number of attributes is hard-coded.
	 */
	if ((code == 1) || (code == 3)) {
		uint32_t sequence;

		ptr[3] = VQP_MAX_ATTRIBUTES;

		sequence = htonl(packet->id);
		memcpy(ptr + 4, &sequence, 4);
	} else {
		if (!original) {
			fr_strerror_printf("Cannot send VQP response without request");
			return -1;
		}

		/*
		 *	Packet Sequence Number
		 */
		memcpy(ptr + 4, original->data + 4, 4);

		ptr[3] = 2;
	}

	ptr += 8;

	/*
	 *	Encode the VP's.
	 */
	for (i = 0; i < VQP_MAX_ATTRIBUTES; i++) {
		if (!vps[i]) break;
		vp = vps[i];

		debug_pair(vp);

		/*
		 *	Type.  Note that we look at only the lower 8
		 *	bits, as the upper 8 bits have been hacked.
		 *	See also dictionary.vqp
		 */
		ptr[0] = 0;
		ptr[1] = 0;
		ptr[2] = 0x0c;
		ptr[3] = vp->attribute & 0xff;

		/* Length */
		ptr[4] = 0;
		ptr[5] = vp->length & 0xff;

		ptr += 6;

		/* Data */
		switch (vp->type) {
		case PW_TYPE_IPADDR:
			memcpy(ptr, &vp->vp_ipaddr, 4);
			break;

		default:
		case PW_TYPE_OCTETS:
		case PW_TYPE_STRING:
			memcpy(ptr, vp->vp_octets, vp->length);
			break;
		}
		ptr += vp->length;
	}

	return 0;
}
Esempio n. 4
0
int vqp_decode(RADIUS_PACKET *packet)
{
	uint8_t *ptr, *end;
	int attribute, length;
	VALUE_PAIR *vp, **tail;

	if (!packet || !packet->data) return -1;

	if (packet->data_len < VQP_HDR_LEN) return -1;

	tail = &packet->vps;

	vp = paircreate(PW_VQP_PACKET_TYPE, PW_TYPE_OCTETS);
	if (!vp) {
		fr_strerror_printf("No memory");
		return -1;
	}
	vp->lvalue = packet->data[1];
	debug_pair(vp);

	*tail = vp;
	tail = &(vp->next);

	vp = paircreate(PW_VQP_ERROR_CODE, PW_TYPE_OCTETS);
	if (!vp) {
		fr_strerror_printf("No memory");
		return -1;
	}
	vp->lvalue = packet->data[2];
	debug_pair(vp);

	*tail = vp;
	tail = &(vp->next);

	vp = paircreate(PW_VQP_SEQUENCE_NUMBER, PW_TYPE_OCTETS);
	if (!vp) {
		fr_strerror_printf("No memory");
		return -1;
	}
	vp->lvalue = packet->id; /* already set by vqp_recv */
	debug_pair(vp);

	*tail = vp;
	tail = &(vp->next);

	ptr = packet->data + VQP_HDR_LEN;
	end = packet->data + packet->data_len;

	/*
	 *	Note that vqp_recv() MUST ensure that the packet is
	 *	formatted in a way we expect, and that vqp_recv() MUST
	 *	be called before vqp_decode().
	 */
	while (ptr < end) {
		attribute = (ptr[2] << 8) | ptr[3];
		length = (ptr[4] << 8) | ptr[5];
		ptr += 6;

		/*
		 *	Hack to get the dictionaries to work correctly.
		 */
		attribute |= 0x2000;
		vp = paircreate(attribute, PW_TYPE_OCTETS);
		if (!vp) {
			pairfree(&packet->vps);

			fr_strerror_printf("No memory");
			return -1;
		}

		switch (vp->type) {
		case PW_TYPE_IPADDR:
			if (length == 4) {
				memcpy(&vp->vp_ipaddr, ptr, 4);
				vp->length = 4;
				break;
			}
			vp->type = PW_TYPE_OCTETS;
			/* FALL-THROUGH */

		default:
		case PW_TYPE_OCTETS:
		case PW_TYPE_STRING:
			vp->length = (length > MAX_VMPS_LEN) ? MAX_VMPS_LEN : length;
			memcpy(vp->vp_octets, ptr, length);
			vp->vp_octets[length] = '\0';
			break;
		}
		ptr += length;
		debug_pair(vp);

		*tail = vp;
		tail = &(vp->next);
	}

	/*
	 *	FIXME: Map attributes to Calling-Station-Id, etc...
	 */

	return 0;
}
Esempio n. 5
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;
}
Esempio n. 6
0
/*
 *	Reply to the request.  Also attach
 *	reply attribute value pairs and any user message provided.
 */
int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
	     const char *secret)
{
	VALUE_PAIR		*reply;
	struct	sockaddr_in	saremote;
	struct	sockaddr_in	*sa;
	const char		*what;
	uint8_t			ip_buffer[16];

	if ((packet->code > 0) && (packet->code < 52)) {
		what = packet_codes[packet->code];
	} else {
		what = "Reply";
	}

	/*
	 *  First time through, allocate room for the packet
	 */
	if (!packet->data) {
		  radius_packet_t	*hdr;
		  uint32_t		lvalue;
		  uint8_t		*ptr, *length_ptr, *vsa_length_ptr;
		  uint8_t		digest[16];
		  int			secretlen;
		  int			vendorcode, vendorpec;
		  u_short		total_length;
		  int			len, allowed;
		  int			msg_auth_offset = 0;

		  /*
		   *	For simplicity in the following logic, we allow
		   *	the attributes to "overflow" the 4k maximum
		   *	RADIUS packet size, by one attribute.
		   */
		  uint8_t		data[MAX_PACKET_LEN + 256];
		  
		  /*
		   *	Use memory on the stack, until we know how
		   *	large the packet will be.
		   */
		  hdr = (radius_packet_t *) data;

		  /*
		   *	Build standard header
		   */
		  hdr->code = packet->code;
		  hdr->id = packet->id;
		  if ((packet->code == PW_ACCOUNTING_REQUEST) ||
		      (packet->code == PW_DISCONNECT_REQUEST)) {
			  memset(hdr->vector, 0, AUTH_VECTOR_LEN);
		  } else {
			  memcpy(hdr->vector, packet->vector, AUTH_VECTOR_LEN);
		  }

		  DEBUG("Sending %s of id %d to %s:%d\n",
			what, packet->id,
			ip_ntoa((char *)ip_buffer, packet->dst_ipaddr),
			packet->dst_port);
		  
		  total_length = AUTH_HDR_LEN;
		  
		  /*
		   *	Load up the configuration values for the user
		   */
		  ptr = hdr->data;
		  vendorcode = 0;
		  vendorpec = 0;
		  vsa_length_ptr = NULL;

		  for (reply = packet->vps; reply; reply = reply->next) {
			  /*
			   *	Ignore non-wire attributes
			   */
			  if ((VENDOR(reply->attribute) == 0) &&
			      ((reply->attribute & 0xFFFF) > 0xff)) {
				  continue;
			  }

			  /*
			   *	Check that the packet is no more than
			   *	4k in size, AFTER over-flowing the 4k
			   *	boundary.  Note that the 'data'
			   *	buffer, above, is one attribute longer
			   *	than necessary, in order to permit
			   *	this overflow.
			   */
			  if (total_length > MAX_PACKET_LEN) {
				  librad_log("ERROR: Too many attributes for packet, result is larger than RFC maximum of 4k");
				  return -1;
			  }

			  /*
			   *	Do stuff for Message-Authenticator
			   */
			  if (reply->attribute == PW_MESSAGE_AUTHENTICATOR) {
				  /*
				   *  Set it to zero!
				   */
				  reply->length = AUTH_VECTOR_LEN;
				  memset(reply->strvalue, 0, AUTH_VECTOR_LEN);
				  msg_auth_offset = total_length;
			  }

			  /*
			   *	Print out ONLY the attributes which
			   *	we're sending over the wire, and print
			   *	them out BEFORE they're encrypted.
			   */
			  debug_pair(reply);

			  /*
			   *	We have a different vendor.  Re-set
			   *	the vendor codes.
			   */
			  if (vendorcode != VENDOR(reply->attribute)) {
				  vendorcode = 0;
				  vendorpec = 0;
				  vsa_length_ptr = NULL;
			  }

			  /*
			   *	If the Vendor-Specific attribute is getting
			   *	full, then create a new VSA attribute
			   *
			   *	FIXME: Multiple VSA's per Vendor-Specific
			   *	SHOULD be configurable.  When that's done,
			   *	the (1), below, can be changed to point to
			   *	a configuration variable which is set TRUE
			   *	if the NAS cannot understand multiple VSA's
			   *	per Vendor-Specific
			   */
			  if ((1) || /* ALWAYS create a new Vendor-Specific */
			      (vsa_length_ptr &&
			       (reply->length + *vsa_length_ptr) >= MAX_STRING_LEN)) {
				  vendorcode = 0;
				  vendorpec = 0;
				  vsa_length_ptr = NULL;
			  }

			  /*
			   *	Maybe we have the start of a set of
			   *	(possibly many) VSA attributes from
			   *	one vendor.  Set a global VSA wrapper
			   */
			  if ((vendorcode == 0) &&
			      ((vendorcode = VENDOR(reply->attribute)) != 0)) {
				  vendorpec  = dict_vendorpec(vendorcode);
				  
				  /*
				   *	This is a potentially bad error...
				   *	we can't find the vendor ID!
				   */
				  if (vendorpec == 0) {
					  /* FIXME: log an error */
					  continue;
				  }

				  /*
				   *	Build a VSA header.
				   */
				  *ptr++ = PW_VENDOR_SPECIFIC;
				  vsa_length_ptr = ptr;
				  *ptr++ = 6;
				  lvalue = htonl(vendorpec);
				  memcpy(ptr, &lvalue, 4);
				  ptr += 4;
				  total_length += 6;
			  }

			  if (vendorpec == VENDORPEC_USR) {
				  lvalue = htonl(reply->attribute & 0xFFFF);
				  memcpy(ptr, &lvalue, 4);

				  length_ptr = vsa_length_ptr;

				  total_length += 4;
				  *length_ptr  += 4;
				  ptr          += 4;

				  /*
				   *	Each USR attribute gets it's own
				   *	VSA wrapper, so we re-set the
				   *	vendor specific information.
				   */
				  vendorcode = 0;
				  vendorpec = 0;
				  vsa_length_ptr = NULL;

			  } else {
				  /*
				   *	All other attributes are as
				   *	per the RFC spec.
				   */

				  *ptr++ = (reply->attribute & 0xFF);
				  length_ptr = ptr;
				  if (vsa_length_ptr) *vsa_length_ptr += 2;
				  *ptr++ = 2;
				  total_length += 2;
			  }
			  
			  switch(reply->type) {
				  
				  /*
				   *	Ascend binary attributes are
				   *	stored internally in binary form.
				   */
			  case PW_TYPE_ABINARY:
			  case PW_TYPE_STRING:
			  case PW_TYPE_OCTETS:
				  /*
				   *  FIXME: HACK for non-updated dictionaries.
				   *  REMOVE in a future release.
				   */
				  if ((strcmp(reply->name, "Ascend-Send-Secret") == 0) ||
				      (strcmp(reply->name, "Ascend-Receive-Secret") == 0)) {
					  reply->flags.encrypt = FLAG_ENCRYPT_ASCEND_SECRET;
				  }
				  if (reply->attribute == PW_USER_PASSWORD) {
					  reply->flags.encrypt = FLAG_ENCRYPT_USER_PASSWORD;
				  }

				  /*
				   *  Encrypt the various password styles
				   */
				  switch (reply->flags.encrypt) {
				  default:
					  break;

				  case FLAG_ENCRYPT_USER_PASSWORD:
				    rad_pwencode((char *)reply->strvalue,
						 &(reply->length),
						 (const char *)secret,
						 (const char *)packet->vector);
				    break;

				  case FLAG_ENCRYPT_TUNNEL_PASSWORD:
					  rad_tunnel_pwencode(reply->strvalue,
							      &(reply->length),
							      secret,
							      packet->vector);
					  break;


				  case FLAG_ENCRYPT_ASCEND_SECRET:
					  make_secret(digest, packet->vector,
						      secret, reply->strvalue);
					  memcpy(reply->strvalue, digest, AUTH_VECTOR_LEN );
					  reply->length = AUTH_VECTOR_LEN;
				  } /* switch over encryption flags */

				  len = reply->length;

				  /*
				   *    Set the TAG at the beginning
				   *    of the string if tagged.  If
				   *    tag value is not valid for
				   *    tagged attribute, make it 0x00
				   *    per RFC 2868.  -cparker
				   */
				  if (reply->flags.has_tag) {
					  if (TAG_VALID(reply->flags.tag)) {
						  len++;
						  *ptr++ = reply->flags.tag;

					  } else if (reply->flags.encrypt == FLAG_ENCRYPT_TUNNEL_PASSWORD) {
						  /*
						   *  Tunnel passwords
						   *  REQUIRE a tag,
						   *  even if we don't
						   *  have a valid
						   *  tag.
						   */
						  len++;
						  *ptr++ = 0x00;
					  } /* else don't write a tag */
				  } /* else the attribute doesn't have a tag */
				 
				  /*
				   *	Ensure we don't go too far.
				   *	The 'length' of the attribute
				   *	may be 0..255, minus whatever
				   *	octets are used in the attribute
				   *	header.
				   */
				  allowed = 255;
				  if (vsa_length_ptr) {
					  allowed -= *vsa_length_ptr;
				  } else {
					  allowed -= *length_ptr;
				  }
				  
				  if (len > allowed) {
					  len = allowed;
				  }
				  
				  *length_ptr += len;
				  if (vsa_length_ptr) *vsa_length_ptr += len;
				  /*
				   *  If we have tagged attributes we can't assume that
				   *  len == reply->length.  Use reply->length for copying
				   *  the string data into the packet.  Use len for the
				   *  true length of the string+tags.
				   */
				  memcpy(ptr, reply->strvalue, reply->length);
				  ptr += reply->length;
				  total_length += len;
				  break;
				  
			  case PW_TYPE_INTEGER:
			  case PW_TYPE_IPADDR:
				  *length_ptr += 4;
				  if (vsa_length_ptr) *vsa_length_ptr += 4;

				  if (reply->type == PW_TYPE_INTEGER ) {
				          /*  If tagged, the tag becomes the MSB of the value */
				          if(reply->flags.has_tag) {
					         /*  Tag must be ( 0x01 -> 0x1F ) OR 0x00  */
					         if(!TAG_VALID(reply->flags.tag)) {
						       reply->flags.tag = 0x00;
						 }
					         lvalue = htonl((reply->lvalue & 0xffffff) |
								((reply->flags.tag & 0xff) << 24));
					  } else {
					         lvalue = htonl(reply->lvalue);
					  }
				  } else {
					  /*
					   *  IP address is already in
					   *  network byte order.
					   */
					  lvalue = reply->lvalue;
				  }
				  memcpy(ptr, &lvalue, 4);
				  ptr += 4;
				  total_length += 4;
				  break;

				  /*
				   *  There are no tagged date attributes.
				   */
			  case PW_TYPE_DATE:
				  *length_ptr += 4;
				  if (vsa_length_ptr) *vsa_length_ptr += 4;

				  lvalue = htonl(reply->lvalue);
				  memcpy(ptr, &lvalue, 4);
				  ptr += 4;
				  total_length += 4;
				  break;
			  default:
				  break;
			  }
		  } /* done looping over all attributes */

		  /*
		   *	Fill in the rest of the fields, and copy
		   *	the data over from the local stack to
		   *	the newly allocated memory.
		   *
		   *	Yes, all this 'memcpy' is slow, but it means
		   *	that we only allocate the minimum amount of
		   *	memory for a request.
		   */
		  packet->data_len = total_length;
		  packet->data = (uint8_t *) malloc(packet->data_len);
		  if (!packet->data) {
			  librad_log("Out of memory");
			  return -1;
		  }
		  memcpy(packet->data, data, packet->data_len);
		  hdr = (radius_packet_t *) packet->data;

		  total_length = htons(total_length);
		  memcpy(hdr->length, &total_length, sizeof(u_short));

		  /*
		   *	If this is not an authentication request, we
		   *	need to calculate the md5 hash over the entire packet
		   *	and put it in the vector.
		   */
		  secretlen = strlen(secret);
		  if (packet->code != PW_AUTHENTICATION_REQUEST &&
		      packet->code != PW_STATUS_SERVER) {
		    MD5_CTX	context;
		      /*
		       *	Set the Message-Authenticator attribute,
		       *	BEFORE setting the reply authentication vector
		       *	for CHALLENGE, ACCEPT and REJECT.
		       */
		      if (msg_auth_offset) {
			      uint8_t calc_auth_vector[AUTH_VECTOR_LEN];

			      switch (packet->code) {
			      default:
				break;
				
			      case PW_AUTHENTICATION_ACK:
			      case PW_AUTHENTICATION_REJECT:
			      case PW_ACCESS_CHALLENGE:
				if (original) {
				  memcpy(hdr->vector, original->vector, AUTH_VECTOR_LEN);
				}
				break;
			      }

			      memset(packet->data + msg_auth_offset + 2, 0,
				     AUTH_VECTOR_LEN);
			      lrad_hmac_md5(packet->data, packet->data_len,
					    secret, secretlen, calc_auth_vector);
			      memcpy(packet->data + msg_auth_offset + 2,
				     calc_auth_vector, AUTH_VECTOR_LEN);
			      memcpy(hdr->vector, packet->vector, AUTH_VECTOR_LEN);
		      }

		      MD5Init(&context);
		      MD5Update(&context, packet->data, packet->data_len);
		      MD5Update(&context, secret, strlen(secret));
		      MD5Final(digest, &context);

		      memcpy(hdr->vector, digest, AUTH_VECTOR_LEN);
		      memcpy(packet->vector, digest, AUTH_VECTOR_LEN);
		  }

		  /*
		   *	Set the Message-Authenticator attribute,
		   *	AFTER setting the authentication vector
		   *	only for ACCESS-REQUESTS
		   */
		  else if (msg_auth_offset) {
			  uint8_t calc_auth_vector[AUTH_VECTOR_LEN];

			  switch (packet->code) {
			  default:
			    break;
			    
			  case PW_AUTHENTICATION_ACK:
			  case PW_AUTHENTICATION_REJECT:
			  case PW_ACCESS_CHALLENGE:
			    if (original) {
				    memcpy(hdr->vector, original->vector,
					   AUTH_VECTOR_LEN);
			    }
			    break;
			  }

			  memset(packet->data + msg_auth_offset + 2,
				 0, AUTH_VECTOR_LEN);
			  lrad_hmac_md5(packet->data, packet->data_len,
					secret, secretlen, calc_auth_vector);
			  memcpy(packet->data + msg_auth_offset + 2,
				  calc_auth_vector, AUTH_VECTOR_LEN);
			  memcpy(hdr->vector, packet->vector, AUTH_VECTOR_LEN);
		  }

		  /*
		   *	If packet->data points to data, then we print out
		   *	the VP list again only for debugging.
		   */
	} else if (librad_debug) {
	  	DEBUG("Re-sending %s of id %d to %s:%d\n", what, packet->id,
		      ip_ntoa((char *)ip_buffer, packet->dst_ipaddr),
		      packet->dst_port);
		
		for (reply = packet->vps; reply; reply = reply->next) {
			/* FIXME: ignore attributes > 0xff */
			debug_pair(reply);
		}
	}
	
	/*
	 *	And send it on it's way.
	 */
	sa = (struct sockaddr_in *) &saremote;
        memset ((char *) sa, '\0', sizeof (saremote));
	sa->sin_family = AF_INET;
	sa->sin_addr.s_addr = packet->dst_ipaddr;
	sa->sin_port = htons(packet->dst_port);

	return sendto(packet->sockfd, packet->data, (int)packet->data_len, 0,
		      (struct sockaddr *)&saremote, sizeof(struct sockaddr_in));
}
Esempio n. 7
0
/*
 *	Calculate/check digest, and decode radius attributes.
 */
int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
	       const char *secret)
{
	DICT_ATTR		*attr;
	uint32_t		lvalue;
	uint32_t		vendorcode;
	VALUE_PAIR		**tail;
	VALUE_PAIR		*pair;
	uint8_t			*ptr;
	int			length;
	int			attribute;
	int			attrlen;
	int			vendorlen;
	radius_packet_t		*hdr;

	hdr = (radius_packet_t *)packet->data;

	/*
	 *	Before we allocate memory for the attributes, do more
	 *	sanity checking.
	 */
	ptr = hdr->data;
	length = packet->data_len - AUTH_HDR_LEN;
	while (length > 0) {
		uint8_t	msg_auth_vector[AUTH_VECTOR_LEN];
		uint8_t calc_auth_vector[AUTH_VECTOR_LEN];

		attrlen = ptr[1];

		switch (ptr[0]) {
		default:	/* don't do anything. */
			break;

			/*
			 *	Note that more than one Message-Authenticator
			 *	attribute is invalid.
			 */
		case PW_MESSAGE_AUTHENTICATOR:
			memcpy(msg_auth_vector, &ptr[2], sizeof(msg_auth_vector));
			memset(&ptr[2], 0, AUTH_VECTOR_LEN);

			switch (packet->code) {
			default:
			  break;

			case PW_AUTHENTICATION_ACK:
			case PW_AUTHENTICATION_REJECT:
			case PW_ACCESS_CHALLENGE:
			  if (original) {
				  memcpy(packet->data + 4, original->vector, AUTH_VECTOR_LEN);
			  }
			  break;
			}

			lrad_hmac_md5(packet->data, packet->data_len,
				      secret, strlen(secret), calc_auth_vector);
			if (memcmp(calc_auth_vector, msg_auth_vector,
				    sizeof(calc_auth_vector)) != 0) {
				char buffer[32];
				librad_log("Received packet from %s with invalid Message-Authenticator!  (Shared secret is incorrect.)",
					   ip_ntoa(buffer, packet->src_ipaddr));
				return 1;
			} /* else the message authenticator was good */

			/*
			 *	Reinitialize Authenticators.
			 */
			memcpy(&ptr[2], msg_auth_vector, AUTH_VECTOR_LEN);
			memcpy(packet->data + 4, packet->vector, AUTH_VECTOR_LEN);
			break;
		} /* switch over the attributes */

		ptr += attrlen;
		length -= attrlen;
	} /* loop over the packet, sanity checking the attributes */

	/*
	 *	Calculate and/or verify digest.
	 */
	switch(packet->code) {
		int rcode;

		case PW_AUTHENTICATION_REQUEST:
		case PW_STATUS_SERVER:
		case PW_DISCONNECT_REQUEST:
			/*
			 *	The authentication vector is random
			 *	nonsense, invented by the client.
			 */
			break;

		case PW_ACCOUNTING_REQUEST:
			if (calc_acctdigest(packet, secret) > 1) {
				char buffer[32];
				librad_log("Received Accounting-Request packet "
				    "from %s with invalid signature!  (Shared secret is incorrect.)",
				    ip_ntoa(buffer, packet->src_ipaddr));
				return 1;
			}
			break;

			/* Verify the reply digest */
		case PW_AUTHENTICATION_ACK:
		case PW_AUTHENTICATION_REJECT:
		case PW_ACCOUNTING_RESPONSE:
			rcode = calc_replydigest(packet, original, secret);
			if (rcode > 1) {
				char buffer[32];
				librad_log("Received %s packet "
					   "from %s with invalid signature (err=%d)!  (Shared secret is incorrect.)",
					   packet_codes[packet->code],
					   ip_ntoa(buffer, packet->src_ipaddr),
					   rcode);
				return 1;
			}
		  break;
	}

	/*
	 *	Extract attribute-value pairs
	 */
	ptr = hdr->data;
	length = packet->data_len - AUTH_HDR_LEN;
	packet->vps = NULL;
	tail = &packet->vps;

	vendorcode = 0;
	vendorlen  = 0;

	while(length > 0) {
		if (vendorlen > 0) {
			attribute = *ptr++ | (vendorcode << 16);
			attrlen   = *ptr++;
		} else {
			attribute = *ptr++;
			attrlen   = *ptr++;
		}

		attrlen -= 2;
		length  -= 2;

		/*
		 *	This could be a Vendor-Specific attribute.
		 *
		 */
		if ((vendorlen <= 0) &&
		    (attribute == PW_VENDOR_SPECIFIC) && 
		    (attrlen > 6)) {
			memcpy(&lvalue, ptr, 4);
			vendorcode = ntohl(lvalue);
			if (vendorcode != 0) {

				if (vendorcode == VENDORPEC_USR) {
					ptr += 4;
					memcpy(&lvalue, ptr, 4);
					/*printf("received USR %04x\n", ntohl(lvalue));*/
					attribute = (ntohl(lvalue) & 0xFFFF) |
							(vendorcode << 16);
					ptr += 4;
					attrlen -= 8;
					length -= 8;

				} else {
					ptr += 4;
					vendorlen = attrlen - 4;
					attribute = *ptr++ | (vendorcode << 16);
					attrlen   = *ptr++;
					attrlen -= 2;
					length -= 6;
				}
			}
			/*
			 *  Else the vendor wasn't found...
			 */
		}

		/*
		 *	FIXME: should we us paircreate() ?
		 */
		if ((pair = malloc(sizeof(VALUE_PAIR))) == NULL) {
			pairfree(&packet->vps);
			librad_log("out of memory");
			errno = ENOMEM;
			return -1;
		}
		
		memset(pair, 0, sizeof(VALUE_PAIR));
		if ((attr = dict_attrbyvalue(attribute)) == NULL) {
			snprintf(pair->name, sizeof(pair->name), "Attr-%d", attribute);
			pair->type = PW_TYPE_OCTETS;
		} else {
			strcpy(pair->name, attr->name);
			pair->type = attr->type;
			pair->flags = attr->flags;
		}
		pair->attribute = attribute;
		pair->length = attrlen;
		pair->operator = T_OP_EQ;
		pair->next = NULL;
		
		switch (pair->type) {
			
		case PW_TYPE_OCTETS:
		case PW_TYPE_ABINARY:
		case PW_TYPE_STRING:
			if (pair->flags.has_tag &&
			    pair->type == PW_TYPE_STRING) {
				int offset = 0;

				if ((pair->length > 0) && TAG_VALID(*ptr)) {
					pair->flags.tag = *ptr;
					pair->length--;
					offset = 1;
				} else if (pair->flags.encrypt == FLAG_ENCRYPT_TUNNEL_PASSWORD) {
					/*
					 * from RFC2868 - 3.5.  Tunnel-Password
					 * If the value of the Tag field is greater than
					 * 0x00 and less than or equal to 0x1F, it SHOULD
					 * be interpreted as indicating which tunnel
					 * (of several alternatives) this attribute pertains;
					 * otherwise, the Tag field SHOULD be ignored.
					 */
					pair->flags.tag = 0x00;
					if (pair->length > 0) pair->length--;
					offset = 1;
				} else {
				       pair->flags.tag = 0x00;
				}

				/*
				 *	pair->length may be zero here...
				 */
				memcpy(pair->strvalue, ptr + offset,
				       pair->length);
			} else {
				/* attrlen always < MAX_STRING_LEN */
				memcpy(pair->strvalue, ptr, attrlen);
			        pair->flags.tag = 0;
			}

			/*
			 *  FIXME: HACK for non-updated dictionaries.
			 *  REMOVE in a future release.
			 */
			if ((strcmp(pair->name, "Ascend-Send-Secret") == 0) ||
			    (strcmp(pair->name, "Ascend-Receive-Secret") == 0)) {
				pair->flags.encrypt = FLAG_ENCRYPT_ASCEND_SECRET;
			}
			if (pair->attribute == PW_USER_PASSWORD) {
				pair->flags.encrypt = FLAG_ENCRYPT_USER_PASSWORD;
			}

			/*
			 *	Decrypt passwords here.
			 */
			switch (pair->flags.encrypt) {
			default:
				break;

				/*
				 *  User-Password
				 */
			case FLAG_ENCRYPT_USER_PASSWORD:
				if (original) {
					rad_pwdecode((char *)pair->strvalue,
						     pair->length, secret,
						     (char *)original->vector);
				} else {
					rad_pwdecode((char *)pair->strvalue,
						     pair->length, secret,
						     (char *)packet->vector);
				}
				if (pair->attribute == PW_USER_PASSWORD) {
					pair->length = strlen(pair->strvalue);
				}
				break;

				/*
				 *  Tunnel-Password
				 */
			case FLAG_ENCRYPT_TUNNEL_PASSWORD:
				if (!original) {
					librad_log("ERROR: Tunnel-Password attribute in request: Cannot decrypt it.");
					return -1;
				}
			        rad_tunnel_pwdecode((char *)pair->strvalue,
						    &pair->length, 
						    secret,
						    (char *)original->vector);
				break;

				/*
				 *  Ascend-Send-Secret
				 *  Ascend-Receive-Secret
				 */
			case FLAG_ENCRYPT_ASCEND_SECRET:
				{
					uint8_t my_digest[AUTH_VECTOR_LEN];
					make_secret(my_digest,
						    original->vector,
						    secret, ptr);
					memcpy(pair->strvalue, my_digest,
					       AUTH_VECTOR_LEN );
					pair->strvalue[AUTH_VECTOR_LEN] = '\0';
					pair->length = strlen(pair->strvalue);
				}
				break;
			} /* switch over encryption flags */
			break;	/* from octets/string/abinary */
			
		case PW_TYPE_INTEGER:
		case PW_TYPE_DATE:
		case PW_TYPE_IPADDR:
			/*
			 *	Check for RFC compliance.  If the
			 *	attribute isn't compliant, turn it
			 *	into a string of raw octets.
			 *
			 *	Also set the lvalue to something
			 *	which should never match anything.
			 */
			if (attrlen != 4) {
				pair->type = PW_TYPE_OCTETS;
				memcpy(pair->strvalue, ptr, attrlen);
				pair->lvalue = 0xbad1bad1;
				break;
			}

      			memcpy(&lvalue, ptr, 4);

			if (attr->type != PW_TYPE_IPADDR) {
				pair->lvalue = ntohl(lvalue);
			} else {
				 /*
				  *  It's an IP address, keep it in network
				  *  byte order, and put the ASCII IP
				  *  address or host name into the string
				  *  value.
				  */
				pair->lvalue = lvalue;
				ip_ntoa(pair->strvalue, pair->lvalue);
			}

			/*
			 *  Only PW_TYPE_INTEGER should have tags.
			 */
			if (pair->flags.has_tag &&
			    pair->type == PW_TYPE_INTEGER) {
			        pair->flags.tag = (pair->lvalue >> 24) & 0xff;
				pair->lvalue &= 0x00ffffff;
			}

			if (attr->type == PW_TYPE_INTEGER) {
				DICT_VALUE *dval;
				dval = dict_valbyattr(pair->attribute,
						      pair->lvalue);
				if (dval) {
					strNcpy(pair->strvalue,
						dval->name,
						sizeof(pair->strvalue));
				}
			}
			break;
			
		default:
			DEBUG("    %s (Unknown Type %d)\n",
			      attr->name,attr->type);
			free(pair);
			pair = NULL;
			break;
		}
		
		if (pair) {
			debug_pair(pair);
			*tail = pair;
			tail = &pair->next;
		}

		ptr += attrlen;
		length -= attrlen;
		if (vendorlen > 0) vendorlen -= (attrlen + 2);
	}