Esempio n. 1
0
/** Convert group membership information into attributes
 *
 * @param[in] inst rlm_ldap configuration.
 * @param[in] request Current request.
 * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect.
 * @param[in] entry retrieved by rlm_ldap_find_user or rlm_ldap_search.
 * @param[in] attr membership attribute to look for in the entry.
 * @return One of the RLM_MODULE_* values.
 */
rlm_rcode_t rlm_ldap_cacheable_userobj(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn,
				       LDAPMessage *entry, char const *attr)
{
	rlm_rcode_t rcode = RLM_MODULE_OK;

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

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

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

	char *name;

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

	int is_dn, i, count;

	rad_assert(entry);
	rad_assert(attr);

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

		return RLM_MODULE_OK;
	}
	count = ldap_count_values_len(values);

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

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

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

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

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

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

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

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

					return rcode;
				}

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

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

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

	ldap_value_free_len(values);
	talloc_free(value_pool);

	if (rcode != RLM_MODULE_OK) return rcode;

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

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

		dn_p++;
	}

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

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

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

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

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

		return -2;
	}

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

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

	if (!head) return 0;

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

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

		(void) talloc_steal(parent, vp);
	}

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

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

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

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

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

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

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

		return 0;
	}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	return 0;
}
Esempio n. 3
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;
}