/*
 *	Convert field X to a VP.
 */
static int csv_map_getvalue(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map, void *uctx)
{
	char const *str = uctx;
	VALUE_PAIR *head = NULL, *vp;
	vp_cursor_t cursor;
	DICT_ATTR const *da;

	rad_assert(ctx != NULL);
	fr_cursor_init(&cursor, &head);

	/*
	 *	FIXME: allow multiple entries.
	 */
	if (map->lhs->type == TMPL_TYPE_ATTR) {
		da = map->lhs->tmpl_da;

	} else {
		char *attr;

		if (tmpl_aexpand(ctx, &attr, request, map->lhs, NULL, NULL) <= 0) {
			RWDEBUG("Failed expanding string");
			return -1;
		}

		da = dict_attrbyname(attr);
		if (!da) {
			RWDEBUG("No such attribute '%s'", attr);
			return -1;
		}

		talloc_free(attr);
	}

	vp = pairalloc(ctx, da);
	rad_assert(vp);

	if (pairparsevalue(vp, str, talloc_array_length(str) - 1) < 0) {
		char *escaped;

		escaped = fr_aprints(vp, str, talloc_array_length(str) - 1, '\'');
		RWDEBUG("Failed parsing value \"%s\" for attribute %s: %s", escaped,
			map->lhs->tmpl_da->name, fr_strerror());

		talloc_free(vp); /* also frees escaped */
		return -1;
	}

	vp->op = map->op;
	fr_cursor_merge(&cursor, vp);

	*out = head;
	return 0;
}
/** Callback for map_to_request
 *
 * Performs exactly the same job as map_to_vp, but pulls attribute values from LDAP entries
 *
 * @see map_to_vp
 */
int rlm_ldap_map_getvalue(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map, void *uctx)
{
	rlm_ldap_result_t *self = uctx;
	VALUE_PAIR *head = NULL, *vp;
	vp_cursor_t cursor;
	int i;

	fr_cursor_init(&cursor, &head);

	switch (map->lhs->type) {
	/*
	 *	This is a mapping in the form of:
	 *		<list>: += <ldap attr>
	 *
	 *	Where <ldap attr> is:
	 *		<list>:<attr> <op> <value>
	 *
	 *	It is to allow for legacy installations which stored
	 *	RADIUS control and reply attributes in separate LDAP
	 *	attributes.
	 */
	case TMPL_TYPE_LIST:
		for (i = 0; i < self->count; i++) {
			vp_map_t *attr = NULL;

			RDEBUG3("Parsing valuepair string \"%s\"", self->values[i]->bv_val);
			if (map_afrom_attr_str(ctx, &attr, self->values[i]->bv_val,
					       map->lhs->tmpl_request, map->lhs->tmpl_list,
					       REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) {
				RWDEBUG("Failed parsing \"%s\" as valuepair (%s), skipping...", fr_strerror(),
					self->values[i]->bv_val);
				continue;
			}

			if (attr->lhs->tmpl_request != map->lhs->tmpl_request) {
				RWDEBUG("valuepair \"%s\" has conflicting request qualifier (%s vs %s), skipping...",
					self->values[i]->bv_val,
					fr_int2str(request_refs, attr->lhs->tmpl_request, "<INVALID>"),
					fr_int2str(request_refs, map->lhs->tmpl_request, "<INVALID>"));
			next_pair:
				talloc_free(attr);
				continue;
			}

			if ((attr->lhs->tmpl_list != map->lhs->tmpl_list)) {
				RWDEBUG("valuepair \"%s\" has conflicting list qualifier (%s vs %s), skipping...",
					self->values[i]->bv_val,
					fr_int2str(pair_lists, attr->lhs->tmpl_list, "<INVALID>"),
					fr_int2str(pair_lists, map->lhs->tmpl_list, "<INVALID>"));
				goto next_pair;
			}

			if (map_to_vp(request, &vp, request, attr, NULL) < 0) {
				RWDEBUG("Failed creating attribute for valuepair \"%s\", skipping...",
					self->values[i]->bv_val);
				goto next_pair;
			}

			fr_cursor_merge(&cursor, vp);
			talloc_free(attr);

			/*
			 *	Only process the first value, unless the operator is +=
			 */
			if (map->op != T_OP_ADD) break;
		}
		break;

	/*
	 *	Iterate over all the retrieved values,
	 *	don't try and be clever about changing operators
	 *	just use whatever was set in the attribute map.
	 */
	case TMPL_TYPE_ATTR:
		for (i = 0; i < self->count; i++) {
			if (!self->values[i]->bv_len) continue;

			vp = pairalloc(ctx, map->lhs->tmpl_da);
			rad_assert(vp);

			if (pairparsevalue(vp, self->values[i]->bv_val, self->values[i]->bv_len) < 0) {
				char *escaped;

				escaped = fr_aprints(vp, self->values[i]->bv_val, self->values[i]->bv_len, '"');
				RWDEBUG("Failed parsing value \"%s\" for attribute %s: %s", escaped,
					map->lhs->tmpl_da->name, fr_strerror());

				talloc_free(vp); /* also frees escaped */
				continue;
			}

			vp->op = map->op;
			fr_cursor_insert(&cursor, vp);

			/*
			 *	Only process the first value, unless the operator is +=
			 */
			if (map->op != T_OP_ADD) break;
		}
		break;

	default:
		rad_assert(0);
	}

	*out = head;

	return 0;
}
Beispiel #3
0
/** Break apart a TLV attribute into individual attributes
 *
 * @param[in] ctx		to allocate new attributes in.
 * @param[in] cursor		to addd new attributes to.
 * @param[in] parent		the current attribute TLV attribute we're processing.
 * @param[in] data		to parse. Points to the data field of the attribute.
 * @param[in] attr_len		length of the TLV attribute.
 * @param[in] data_len		remaining data in the packet.
 * @param[in] decoder_ctx	IVs, keys etc...
 * @return
 *	- Length on success.
 *	- < 0 on malformed attribute.
 */
static ssize_t sim_decode_tlv(TALLOC_CTX *ctx, fr_cursor_t *cursor,
			      fr_dict_attr_t const *parent,
			      uint8_t const *data, size_t const attr_len, size_t data_len,
			      void *decoder_ctx)
{
	uint8_t const		*p = data, *end = p + attr_len;
	uint8_t			*decr = NULL;
	ssize_t			decr_len;
	fr_dict_attr_t const	*child;
	VALUE_PAIR		*head = NULL;
	fr_cursor_t		tlv_cursor;
	ssize_t			rcode;

	if (data_len < 2) {
		fr_strerror_printf("%s: Insufficient data", __FUNCTION__);
		return -1; /* minimum attr size */
	}

	/*
	 *	We have an AES-128-CBC encrypted attribute
	 *
	 *	IV is from AT_IV, key is from k_encr.
	 *
	 *	unfortunately the ordering of these two attributes
	 *	aren't specified, so we may have to hunt for the IV.
	 */
	if (parent->flags.encrypt) {
		FR_PROTO_TRACE("found encrypted attribute '%s'", parent->name);

		decr_len = sim_value_decrypt(ctx, &decr, p + 2,
					     attr_len - 2, data_len - 2, decoder_ctx);	/* Skip reserved */
		if (decr_len < 0) return -1;

		p = decr;
		end = p + decr_len;
	} else {
		p += 2;	/* Skip the reserved bytes */
	}

	FR_PROTO_HEX_DUMP(p, end - p, "tlvs");

	/*
	 *  Record where we were in the list when packet_ctx function was called
	 */
	fr_cursor_init(&tlv_cursor, &head);
	while ((size_t)(end - p) >= sizeof(uint32_t)) {
		uint8_t	sim_at = p[0];
		size_t	sim_at_len = ((size_t)p[1]) << 2;

		if ((p + sim_at_len) > end) {
			fr_strerror_printf("%s: Malformed nested attribute %d: Length field (%zu bytes) value "
					   "longer than remaining data in parent (%zu bytes)",
					   __FUNCTION__, sim_at, sim_at_len, end - p);

		error:
			talloc_free(decr);
			fr_pair_list_free(&head);
			return -1;
		}

		if (sim_at_len == 0) {
			fr_strerror_printf("%s: Malformed nested attribute %d: Length field 0", __FUNCTION__, sim_at);
			goto error;
		}

		/*
		 *	Padding attributes are cleartext inside of
		 *	encrypted TLVs to pad out the value to the
		 *	correct length for the block cipher
		 *	(16 in the case of AES-128-CBC).
		 */
		if (sim_at == FR_SIM_PADDING) {
			uint8_t zero = 0;
			uint8_t i;

			if (!parent->flags.encrypt) {
				fr_strerror_printf("%s: Found padding attribute outside of an encrypted TLV",
						   __FUNCTION__);
				goto error;
			}

			if (!fr_cond_assert(data_len % 4)) goto error;

			if (sim_at_len > 12) {
				fr_strerror_printf("%s: Expected padding attribute length <= 12 bytes, got %zu bytes",
						   __FUNCTION__, sim_at_len);
				goto error;
			}

			/*
			 *	RFC says we MUST verify that FR_SIM_PADDING
			 *	data is zeroed out.
			 */
			for (i = 2; i < sim_at_len; i++) zero |= p[i];
			if (zero) {
				fr_strerror_printf("%s: Padding attribute value not zeroed 0x%pH", __FUNCTION__,
						   fr_box_octets(p + 2, sim_at_len - 2));
				goto error;
			}

			p += sim_at_len;
			continue;
		}

		child = fr_dict_attr_child_by_num(parent, p[0]);
		if (!child) {
			fr_dict_attr_t const *unknown_child;

			FR_PROTO_TRACE("Failed to find child %u of TLV %s", p[0], parent->name);

			/*
			 *	Encountered none skippable attribute
			 *
			 *	RFC says we need to die on these if we don't
			 *	understand them.  non-skippables are < 128.
			 */
			if (sim_at <= SIM_SKIPPABLE_MAX) {
				fr_strerror_printf("%s: Unknown (non-skippable) attribute %i",
						   __FUNCTION__, sim_at);
				goto error;
			}

			/*
			 *	Build an unknown attr
			 */
			unknown_child = fr_dict_unknown_afrom_fields(ctx, parent,
								     fr_dict_vendor_num_by_da(parent), p[0]);
			if (!unknown_child) goto error;
			child = unknown_child;
		}
		FR_PROTO_TRACE("decode context changed %s -> %s", parent->name, child->name);

		rcode = sim_decode_pair_value(ctx, &tlv_cursor, child, p + 2, sim_at_len - 2, (end - p) - 2,
					      decoder_ctx);
		if (rcode < 0) goto error;
		p += sim_at_len;
	}
	fr_cursor_head(&tlv_cursor);
	fr_cursor_tail(cursor);
	fr_cursor_merge(cursor, &tlv_cursor);	/* Wind to the end of the new pairs */
	talloc_free(decr);

	return attr_len;
}
Beispiel #4
0
/*
 *	Convert diameter attributes to our VALUE_PAIR's
 */
static VALUE_PAIR *diameter2vp(REQUEST *request, REQUEST *fake, SSL *ssl,
			       uint8_t const *data, size_t data_len)
{
	uint32_t	attr;
	uint32_t	vendor;
	uint32_t	length;
	size_t		offset;
	size_t		size;
	size_t		data_left = data_len;
	VALUE_PAIR	*first = NULL;
	VALUE_PAIR	*vp;
	RADIUS_PACKET	*packet = fake->packet; /* FIXME: api issues */
	vp_cursor_t	out;

	fr_cursor_init(&out, &first);

	while (data_left > 0) {
		rad_assert(data_left <= data_len);
		memcpy(&attr, data, sizeof(attr));
		data += 4;
		attr = ntohl(attr);
		vendor = 0;

		memcpy(&length, data, sizeof(length));
		data += 4;
		length = ntohl(length);

		/*
		 *	A "vendor" flag, with a vendor ID of zero,
		 *	is equivalent to no vendor.  This is stupid.
		 */
		offset = 8;
		if ((length & ((uint32_t)1 << 31)) != 0) {
			memcpy(&vendor, data, sizeof(vendor));
			vendor = ntohl(vendor);

			data += 4; /* skip the vendor field, it's zero */
			offset += 4; /* offset to value field */

			if (attr > 65535) goto next_attr;
			if (vendor > FR_MAX_VENDOR) goto next_attr;
		}

		/*
		 *	FIXME: Handle the M bit.  For now, we assume that
		 *	some other module takes care of any attribute
		 *	with the M bit set.
		 */

		/*
		 *	Get the length.
		 */
		length &= 0x00ffffff;

		/*
		 *	Get the size of the value portion of the
		 *	attribute.
		 */
		size = length - offset;

		/*
		 *	Vendor attributes can be larger than 255.
		 *	Normal attributes cannot be.
		 */
		if ((attr > 255) && (vendor == 0)) {
			RWDEBUG2("Skipping Diameter attribute %u", attr);
			goto next_attr;
		}

		/*
		 *	EAP-Message AVPs can be larger than 253 octets.
		 *
		 *	For now, we rely on the main decoder in
		 *	src/lib/radius to decode data into VPs.  This
		 *	means putting the data into a RADIUS attribute
		 *	format.  It also means that we can't handle
		 *	"extended" attributes in the Diameter space.  Oh well...
		 */
		if ((size > 253) && !((vendor == 0) && (attr == PW_EAP_MESSAGE))) {
			RWDEBUG2("diameter2vp skipping long attribute %u", attr);
			goto next_attr;
		}

		/*
		 *	RADIUS VSAs are handled as Diameter attributes
		 *	with Vendor-Id == 0, and the VSA data packed
		 *	into the "String" field as per normal.
		 *
		 *	EXCEPT for the MS-CHAP attributes.
		 */
		if ((vendor == 0) && (attr == PW_VENDOR_SPECIFIC)) {
			ssize_t decoded;
			uint8_t buffer[256];

			buffer[0] = PW_VENDOR_SPECIFIC;
			buffer[1] = size + 2;
			memcpy(buffer + 2, data, size);

			vp = NULL;
			decoded = rad_attr2vp(packet, NULL, NULL, NULL,
					      buffer, size + 2, &vp);
			if (decoded < 0) {
				REDEBUG2("diameter2vp failed decoding attr: %s",
					fr_strerror());
				goto do_octets;
			}

			if ((size_t) decoded != size + 2) {
				REDEBUG2("diameter2vp failed to entirely decode VSA");
				fr_pair_list_free(&vp);
				goto do_octets;
			}

			fr_cursor_merge(&out, vp);

			goto next_attr;
		}

		/*
		 *	Create it.  If this fails, it's because we're OOM.
		 */
	do_octets:
		vp = fr_pair_afrom_num(packet, attr, vendor);
		if (!vp) {
			RDEBUG2("Failure in creating VP");
			fr_pair_list_free(&first);
			return NULL;
		}

		/*
		 *	If it's a type from our dictionary, then
		 *	we need to put the data in a relevant place.
		 *
		 *	@todo: Export the lib/radius.c decoder, and use it here!
		 */
		switch (vp->da->type) {
		case PW_TYPE_INTEGER:
		case PW_TYPE_DATE:
			if (size != vp->vp_length) {
				DICT_ATTR const *da;

				/*
				 *	Bad format.  Create a "raw"
				 *	attribute.
				 */
		raw:
				if (vp) fr_pair_list_free(&vp);
				da = dict_unknown_afrom_fields(packet, attr, vendor);
				if (!da) return NULL;
				vp = fr_pair_afrom_da(packet, da);
				if (!vp) return NULL;
				fr_pair_value_memcpy(vp, data, size);
				break;
			}
			memcpy(&vp->vp_integer, data, vp->vp_length);

			/*
			 *	Stored in host byte order: change it.
			 */
			vp->vp_integer = ntohl(vp->vp_integer);
			break;

		case PW_TYPE_INTEGER64:
			if (size != vp->vp_length) goto raw;
			memcpy(&vp->vp_integer64, data, vp->vp_length);

			/*
			 *	Stored in host byte order: change it.
			 */
			vp->vp_integer64 = ntohll(vp->vp_integer64);
			break;

		case PW_TYPE_IPV4_ADDR:
			if (size != vp->vp_length) {
				RDEBUG2("Invalid length attribute %d",
				       attr);
				fr_pair_list_free(&first);
				fr_pair_list_free(&vp);
				return NULL;
			}
			memcpy(&vp->vp_ipaddr, data, vp->vp_length);

			/*
			 *	Stored in network byte order: don't change it.
			 */
			break;

		case PW_TYPE_BYTE:
			if (size != vp->vp_length) goto raw;
			vp->vp_byte = data[0];
			break;

		case PW_TYPE_SHORT:
			if (size != vp->vp_length) goto raw;
			vp->vp_short = (data[0] * 256) + data[1];
			break;

		case PW_TYPE_SIGNED:
			if (size != vp->vp_length) goto raw;
			memcpy(&vp->vp_signed, data, vp->vp_length);
			vp->vp_signed = ntohl(vp->vp_signed);
			break;

		case PW_TYPE_IPV6_ADDR:
			if (size != vp->vp_length) goto raw;
			memcpy(&vp->vp_ipv6addr, data, vp->vp_length);
			break;

		case PW_TYPE_IPV6_PREFIX:
			if (size != vp->vp_length) goto raw;
			memcpy(vp->vp_ipv6prefix, data, vp->vp_length);
			break;

		case PW_TYPE_STRING:
			fr_pair_value_bstrncpy(vp, data, size);
			vp->vp_length = strlen(vp->vp_strvalue); /* embedded zeros are NOT allowed */
			break;

			/*
			 *	Copy it over verbatim.
			 */
		case PW_TYPE_OCTETS:
		default:
			fr_pair_value_memcpy(vp, data, size);
			break;
		}

		/*
		 *	Ensure that the client is using the
		 *	correct challenge.  This weirdness is
		 *	to protect against against replay
		 *	attacks, where anyone observing the
		 *	CHAP exchange could pose as that user,
		 *	by simply choosing to use the same
		 *	challenge.
		 *
		 *	By using a challenge based on
		 *	information from the current session,
		 *	we can guarantee that the client is
		 *	not *choosing* a challenge.
		 *
		 *	We're a little forgiving in that we
		 *	have loose checks on the length, and
		 *	we do NOT check the Id (first octet of
		 *	the response to the challenge)
		 *
		 *	But if the client gets the challenge correct,
		 *	we're not too worried about the Id.
		 */
		if (((vp->da->vendor == 0) && (vp->da->attr == PW_CHAP_CHALLENGE)) ||
		    ((vp->da->vendor == VENDORPEC_MICROSOFT) && (vp->da->attr == PW_MSCHAP_CHALLENGE))) {
			uint8_t	challenge[16];

			if ((vp->vp_length < 8) ||
			    (vp->vp_length > 16)) {
				RDEBUG("Tunneled challenge has invalid length");
				fr_pair_list_free(&first);
				fr_pair_list_free(&vp);
				return NULL;
			}

			eapttls_gen_challenge(ssl, challenge,
					      sizeof(challenge));

			if (memcmp(challenge, vp->vp_octets,
				   vp->vp_length) != 0) {
				RDEBUG("Tunneled challenge is incorrect");
				fr_pair_list_free(&first);
				fr_pair_list_free(&vp);
				return NULL;
			}
		}

		/*
		 *	Update the list.
		 */
		fr_cursor_insert(&out, vp);

	next_attr:
		/*
		 *	Catch non-aligned attributes.
		 */
		if (data_left == length) break;

		/*
		 *	The length does NOT include the padding, so
		 *	we've got to account for it here by rounding up
		 *	to the nearest 4-byte boundary.
		 */
		length += 0x03;
		length &= ~0x03;

		rad_assert(data_left >= length);
		data_left -= length;
		data += length - offset; /* already updated */
	}

	/*
	 *	We got this far.  It looks OK.
	 */
	return first;
}
Beispiel #5
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;
}
Beispiel #6
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;
}