27RCSID(
"$Id: 12a5e576be06a1f30bd1d54f1fa677c80592a166 $")
31#include <freeradius-devel/util/debug.h>
32#include <freeradius-devel/server/rcode.h>
33#include <freeradius-devel/unlang/action.h>
35#define LOG_PREFIX "rlm_ldap groups"
120 if (!
inst->group.obj_name_attr) {
121 REDEBUG(
"Told to convert group names to DNs but missing 'group.name_attribute' directive");
125 REDEBUG(
"Missing group base_dn");
129 RDEBUG2(
"Converting group name(s) to group DN(s)");
136 inst->group.obj_filter ?
"(&" :
"",
137 inst->group.obj_filter ?
inst->group.obj_filter :
"",
141 filter = talloc_asprintf_append_buffer(filter,
"(%s=%s)",
inst->group.obj_name_attr,
buffer);
145 filter = talloc_asprintf_append_buffer(filter,
"%s%s",
146 inst->group.obj_filter ?
")" :
"",
150 group_ctx->
base_dn->vb_strvalue,
inst->group.obj_scope, filter,
167 unsigned int entry_cnt;
173 switch (query->
ret) {
179 RDEBUG2(
"Tried to resolve group name(s) to DNs but got no results");
188 if (entry_cnt > group_ctx->
name_cnt) {
189 REDEBUG(
"Number of DNs exceeds number of names, group and/or dn should be more restrictive");
195 if (entry_cnt < group_ctx->name_cnt) {
196 RWDEBUG(
"Got partial mapping of group names (%i) to DNs (%i), membership information may be incomplete",
202 ldap_get_option(query->
ldap_conn->
handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
203 REDEBUG(
"Failed retrieving entry: %s", ldap_err2string(ldap_errno));
212 ldap_get_option(query->
ldap_conn->
handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
213 REDEBUG(
"Retrieving object DN from entry failed: %s", ldap_err2string(ldap_errno));
220 RDEBUG2(
"Got group DN \"%s\"", dn);
253 if (!
inst->group.obj_name_attr) {
254 REDEBUG(
"Told to resolve group DN to name but missing 'group.name_attribute' directive");
258 RDEBUG2(
"Resolving group DN \"%s\" to group name", *group_ctx->
dn);
261 LDAP_SCOPE_BASE, NULL, group_ctx->
attrs, NULL, NULL);
280 struct berval **values = NULL;
285 switch (query->
ret) {
291 REDEBUG(
"Group DN \"%s\" did not resolve to an object", *group_ctx->
dn);
302 ldap_get_option(query->
ldap_conn->
handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
303 REDEBUG(
"Failed retrieving entry: %s", ldap_err2string(ldap_errno));
310 REDEBUG(
"No %s attributes found in object",
inst->group.obj_name_attr);
318 RDEBUG2(
"Group DN \"%s\" resolves to name \"%pV\"", *group_ctx->
dn, &
vp->data);
327 if (values) ldap_value_free_len(values);
349 RDEBUG2(
"Adding cacheable user object memberships");
383 switch (p_result->
rcode) {
397 if (*group_ctx->
dn) {
445 LDAPMessage *entry = autz_ctx->
entry;
448 struct berval **values;
452 int is_dn, i,
count, name2dn = 0, dn2name = 0;
462 RDEBUG2(
"No cacheable group memberships found in user object");
466 count = ldap_count_values_len(values);
473 group_ctx->
ttrunk = ttrunk;
491 for (i = 0; (i <
count); i++) {
494 if (
inst->group.cacheable_dn) {
508 REDEBUG(
"Too many groups require name to DN resolution");
510 ldap_value_free_len(values);
518 if (
inst->group.cacheable_name) {
532 REDEBUG(
"Too many groups require DN to name resolution");
540 ldap_value_free_len(values);
548 group_ctx->
attrs[0] =
inst->group.obj_name_attr;
585 group_ctx->
attrs[0] =
inst->group.obj_name_attr;
587 group_ctx->
base_dn->vb_strvalue,
inst->group.obj_scope,
588 filter->vb_strvalue, group_ctx->
attrs, NULL, NULL);
624 switch (query->
ret) {
630 RDEBUG2(
"No cacheable group memberships found in group objects");
641 ldap_get_option(query->
ldap_conn->
handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
642 REDEBUG(
"Failed retrieving entry: %s", ldap_err2string(ldap_errno));
647 RDEBUG2(
"Adding cacheable group object memberships");
649 if (
inst->group.cacheable_dn) {
652 ldap_get_option(query->
ldap_conn->
handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
653 REDEBUG(
"Retrieving object DN from entry failed: %s", ldap_err2string(ldap_errno));
668 if (
inst->group.cacheable_name) {
669 struct berval **values;
672 if (!values)
continue;
681 ldap_value_free_len(values);
703 if (!
inst->group.obj_membership_filter) {
704 RDEBUG2(
"Skipping caching group objects as directive 'group.membership_filter' is not set");
709 REDEBUG(
"Missing group base_dn");
750 switch (query->
ret) {
754 LDAPMessage *entry = NULL;
806 char const *filters[] = { name_filter,
inst->group.obj_filter,
inst->group.obj_membership_filter };
809 if (!
inst->group.obj_name_attr) {
810 REDEBUG(
"Told to search for group by name, but missing 'group.name_attribute' "
829 .always_escape =
false,
836 snprintf(name_filter,
sizeof(name_filter),
"(%s=%s)",
837 inst->group.obj_name_attr,
xlat_ctx->group->vb_strvalue);
872 if (!
inst->group.obj_name_attr) {
873 REDEBUG(
"Told to resolve group DN to name but missing 'group.name_attribute' directive");
908 LDAP_SCOPE_BASE, NULL,
xlat_ctx->attrs, NULL, NULL);
924 bool value_is_dn =
false;
926 char *value_name = NULL;
935 ldap_get_option(query->
ldap_conn->
handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
936 REDEBUG(
"Failed retrieving entry: %s", ldap_err2string(ldap_errno));
942 RDEBUG2(
"User object contains no group membership information in attribute \"%s\"",
943 inst->group.userobj_membership_attr);
951 group_ctx->
count = ldap_count_values_len(group_ctx->
values);
958 if (group_ctx->
query) {
960 struct berval **values = NULL;
968 REDEBUG(
"Group DN \"%pV\" did not resolve to an object",
979 REDEBUG(
"Failed retrieving entry: %s", ldap_err2string(ldap_errno));
985 REDEBUG(
"No %s attributes found in object",
inst->group.obj_name_attr);
992 ldap_value_free_len(values);
1012 if (((talloc_array_length(value_name) - 1) == group->vb_length) &&
1013 (memcmp(group->vb_strvalue, value_name, group->vb_length) == 0)) {
1014 RDEBUG2(
"User found in group \"%pV\". Comparison between membership: name "
1015 "(resolved from DN \"%pV\"), check: name", group,
1021 TALLOC_FREE(value_name);
1029 RDEBUG2(
"Processing %s value \"%pV\" as a %s",
inst->group.userobj_membership_attr,
1031 value_is_dn ?
"DN" :
"group name");
1036 if (!
xlat_ctx->group_is_dn && !value_is_dn) {
1037 if ((group->vb_length ==
value->bv_len) &&
1038 (memcmp(
value->bv_val, group->vb_strvalue,
value->bv_len) == 0)) {
1039 RDEBUG2(
"User found in group \"%pV\". Comparison between membership: name, check: name",
1050 if (
xlat_ctx->group_is_dn && value_is_dn) {
1052 RDEBUG2(
"User found in group DN \"%pV\". "
1053 "Comparison between membership: dn, check: dn", group);
1064 if (!value_is_dn &&
xlat_ctx->group_is_dn) {
1070 group_ctx->
lookup_dn = group->vb_strvalue;
1084 if (((talloc_array_length(group_ctx->
group_name) - 1) ==
value->bv_len) &&
1086 RDEBUG2(
"User found in group \"%pV\". Comparison between membership: "
1087 "name, check: name (resolved from DN \"%pV\")",
1099 if (value_is_dn && !
xlat_ctx->group_is_dn) {
1128 if (group_ctx->
values) ldap_value_free_len(group_ctx->
values);
1149 .
attrs = {
inst->group.obj_name_attr, NULL }
1152 RDEBUG2(
"Checking user object's %s attributes",
inst->group.userobj_membership_attr);
1200 RDEBUG2(
"User found. Matched cached membership");
1207 RDEBUG2(
"Cached membership not found");
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
@ UNLANG_ACTION_PUSHED_CHILD
unlang_t pushed a new child onto the stack, execute it instead of continuing.
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
static int const char char buffer[256]
#define USES_APPLE_DEPRECATED_API
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
#define unlang_function_push_with_result(_result_p, _request, _func, _repeat, _signal, _sigmask, _top_frame, _uctx)
Push a generic function onto the unlang stack that produces a result.
#define unlang_function_repeat_set(_request, _repeat)
Set a new repeat function for an existing function frame.
fr_ldap_query_t * query
Current query performing group lookup.
static unlang_action_t ldap_check_userobj_start(UNUSED unlang_result_t *p_result, request_t *request, void *uctx)
Initiate a user lookup to check membership.
unlang_action_t rlm_ldap_cacheable_groupobj(unlang_result_t *p_result, request_t *request, ldap_autz_ctx_t *autz_ctx)
Convert group membership information into attributes.
static char const * null_attrs[]
char const * attrs[2]
For retrieving the group name.
static unlang_action_t ldap_cacheable_groupobj_start(unlang_result_t *p_result, request_t *request, void *uctx)
Initiate an LDAP search for group membership looking at the group objects.
static unlang_action_t ldap_group_name2dn_start(unlang_result_t *p_result, request_t *request, void *uctx)
Convert multiple group names into a DNs.
ldap_group_xlat_ctx_t * xlat_ctx
Xlat context being evaluated.
int count
How many entries there are in values.
unlang_action_t rlm_ldap_check_groupobj_dynamic(unlang_result_t *p_result, request_t *request, ldap_group_xlat_ctx_t *xlat_ctx)
Initiate an LDAP search to determine group membership, querying group objects.
void * uctx
Optional context for use in results parsing.
static int userobj_dyn_free(ldap_group_userobj_dyn_ctx_t *group_ctx)
Ensure retrieved LDAP values are cleared up.
char * group_name[LDAP_MAX_CACHEABLE+1]
List of group names which need resolving.
char * group_dn[LDAP_MAX_CACHEABLE+1]
List of group DNs which need resolving.
struct berval ** values
Values of the membership attribute to check.
static unlang_action_t ldap_check_userobj_resume(unlang_result_t *p_result, request_t *request, void *uctx)
Process the results of evaluating a user object when checking group membership.
unlang_action_t rlm_ldap_cacheable_userobj(unlang_result_t *p_result, request_t *request, ldap_autz_ctx_t *autz_ctx, char const *attr)
Convert group membership information into attributes.
static unlang_action_t ldap_cacheable_userobj_resolve(unlang_result_t *p_result, request_t *request, void *uctx)
Initiate DN to name and name to DN group lookups.
tmpl_t * filter_tmpl
Tmpl to expand into LDAP filter.
char const * attrs[2]
For retrieving the group name.
rlm_ldap_t const * inst
Module instance.
unlang_action_t rlm_ldap_check_userobj_dynamic(unlang_result_t *p_result, request_t *request, ldap_group_xlat_ctx_t *xlat_ctx)
Query the LDAP directory to check if a user object is a member of a group.
fr_value_box_list_t expanded_filter
Values produced by expanding filter xlat.
fr_value_box_t * base_dn
The base DN to search for groups in.
fr_ldap_thread_trunk_t * ttrunk
Trunk on which to perform additional queries.
fr_pair_list_t groups
Temporary list to hold pairs.
static unlang_action_t ldap_cacheable_groupobj_resume(unlang_result_t *p_result, request_t *request, void *uctx)
Process the results of a group object lookup.
fr_ldap_thread_trunk_t * ttrunk
Trunk on which to perform additional queries.
char ** dn
Current DN being resolved.
static unlang_action_t ldap_cacheable_userobj_store(unlang_result_t *p_result, request_t *request, ldap_group_userobj_ctx_t *group_ctx)
Move user object group attributes to the control list.
static unlang_action_t ldap_group_name2dn_resume(unlang_result_t *p_result, request_t *request, void *uctx)
Process the results of looking up group DNs from names.
char const * attrs[2]
For resolving name from DN.
static unlang_action_t ldap_group_dn2name_resume(unlang_result_t *p_result, request_t *request, void *uctx)
Process the results of a group DN -> name lookup.
TALLOC_CTX * list_ctx
In which to allocate pairs.
bool resolving_value
Is the current query resolving a DN from values.
unsigned int name_cnt
How many names need resolving.
static unlang_action_t ldap_dn2name_start(unlang_result_t *p_result, request_t *request, void *uctx)
Initiate resolving a group DN to its name.
static void ldap_group_userobj_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
Cancel a pending group lookup query.
static void ldap_group_groupobj_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
Cancel a pending group object lookup.
static unlang_action_t ldap_check_groupobj_resume(unlang_result_t *p_result, request_t *request, void *uctx)
Process the results of a group object lookup.
fr_value_box_t * base_dn
The base DN to search for groups in.
static void ldap_dn2name_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
Cancel an in-progress DN to name lookup.
unlang_action_t rlm_ldap_check_cached(unlang_result_t *p_result, rlm_ldap_t const *inst, request_t *request, fr_value_box_t const *check)
Check group membership attributes to see if a user is a member.
static unlang_action_t ldap_group_dn2name_start(unlang_result_t *p_result, request_t *request, void *uctx)
Initiate an LDAP search to turn a group DN into it's name.
fr_ldap_query_t * query
Current query performing group resolution.
char const * lookup_dn
The DN currently being looked up, when resolving DN to name.
int value_no
The current entry in values being processed.
rlm_ldap_t const * inst
Module instance.
char * group_name
Result of resolving the provided group DN as to a name.
fr_ldap_query_t * query
Current query doing a DN to name resolution.
Context to use when looking up group membership using group objects.
Context to use when resolving group membership from the user object.
Context to use when evaluating group membership from the user object in an xlat.
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
rlm_rcode_t rcode
The current rcode, from executing the instruction or merging the result from a frame.
size_t fr_ldap_util_normalise_dn(char *out, char const *in)
Normalise escape sequences in a DN.
size_t fr_ldap_uri_escape_func(UNUSED request_t *request, char *out, size_t outlen, char const *in, UNUSED void *arg))
Converts "bad" strings into ones which are safe for LDAP.
int fr_ldap_filter_to_tmpl(TALLOC_CTX *ctx, tmpl_rules_t const *t_rules, char const **sub, size_t sublen, tmpl_t **out))
Combine filters and tokenize to a tmpl.
#define LDAP_MAX_FILTER_STR_LEN
Maximum length of an xlat expanded filter.
bool fr_ldap_util_is_dn(char const *in, size_t inlen)
Check whether a string looks like a DN.
LDAP * handle
libldap handle.
fr_ldap_result_code_t ret
Result code.
#define LDAP_MAX_CACHEABLE
Maximum number of groups we retrieve from the server for a given user which need resolving from name ...
trunk_request_t * treq
Trunk request this query is associated with.
char * fr_ldap_berval_to_string(TALLOC_CTX *ctx, struct berval const *in)
Convert a berval to a talloced string.
int fr_ldap_box_escape(fr_value_box_t *vb, UNUSED void *uctx)
fr_ldap_connection_t * ldap_conn
LDAP connection this query is running on.
@ LDAP_RESULT_SUCCESS
Successfully got LDAP results.
@ LDAP_RESULT_NO_RESULT
No results returned.
@ LDAP_RESULT_BAD_DN
The requested DN does not exist.
static int fr_ldap_berval_strncasecmp(struct berval *value, char const *str, size_t strlen)
Compare a berval with a C string of a known length using case insensitive comparison.
LDAPMessage * result
Head of LDAP results list.
#define LDAP_MAX_GROUP_NAME_LEN
Maximum name of a group name.
Thread LDAP trunk structure.
LDAP * fr_ldap_handle_thread_local(void)
Get a thread local dummy LDAP handle.
unlang_action_t fr_ldap_trunk_search(TALLOC_CTX *ctx, fr_ldap_query_t **out, request_t *request, fr_ldap_thread_trunk_t *ttrunk, char const *base_dn, int scope, char const *filter, char const *const *attrs, LDAPControl **serverctrls, LDAPControl **clientctrls)
Run an async search LDAP query on a trunk connection.
#define REXDENT()
Exdent (unindent) R* messages by one level.
#define RINDENT()
Indent R* messages by one level.
@ FR_TYPE_STRING
String of printable characters.
int fr_pair_value_strdup(fr_pair_t *vp, char const *src, bool tainted)
Copy data into an "string" data type.
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
int fr_pair_value_bstrndup(fr_pair_t *vp, char const *src, size_t len, bool tainted)
Copy data into a "string" type value pair.
#define RDEBUG_ENABLED2()
#define RETURN_UNLANG_INVALID
#define RETURN_UNLANG_RCODE(_rcode)
#define RETURN_UNLANG_NOTFOUND
#define RETURN_UNLANG_FAIL
#define RETURN_UNLANG_REJECT
rlm_rcode_t
Return codes indicating the result of the module call.
@ RLM_MODULE_INVALID
The module considers the request invalid.
@ RLM_MODULE_OK
The module is OK, continue.
@ RLM_MODULE_FAIL
Module failed, don't reply.
@ RLM_MODULE_NOTFOUND
User not found.
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
fr_dict_attr_t const * request_attr_request
fr_dict_attr_t const * request_attr_control
LDAP authorization and authentication module headers.
ldap_autz_call_env_t * call_env
fr_ldap_thread_trunk_t * ttrunk
tmpl_t * group_filter
tmpl to expand as group membership filter.
struct rlm_ldap_t::@174 group
fr_value_box_t group_base
Base DN in which to search for groups.
Holds state of in progress async authorization.
Holds state of in progress group membership check xlat.
#define pair_append_control(_attr, _da)
Allocate and append a fr_pair_t to the control list.
fr_pair_list_t * tmpl_list_head(request_t *request, fr_dict_attr_t const *list)
Resolve attribute fr_pair_list_t value to an attribute list.
TALLOC_CTX * tmpl_list_ctx(request_t *request, fr_dict_attr_t const *list)
Return the correct TALLOC_CTX to alloc fr_pair_t in, for a list.
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
struct tmpl_rules_s tmpl_rules_t
Optional arguments passed to vp_tmpl functions.
fr_signal_t
Signals that can be generated/processed by request signal handlers.
@ FR_SIGNAL_CANCEL
Request has been cancelled.
static char buff[sizeof("18446744073709551615")+3]
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
eap_aka_sim_process_conf_t * inst
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Stores an attribute, a value and various bits of other data.
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
static int talloc_const_free(void const *ptr)
Free const'd memory.
int unlang_tmpl_push(TALLOC_CTX *ctx, fr_value_box_list_t *out, request_t *request, tmpl_t const *tmpl, unlang_tmpl_args_t *args)
Push a tmpl onto the stack for evaluation.
@ TMPL_ESCAPE_PRE_CONCAT
Pre-concatenation escaping is useful for DSLs where elements of the expansion are static,...
void trunk_request_signal_cancel(trunk_request_t *treq)
Cancel a trunk request.
#define fr_pair_dcursor_by_da_init(_cursor, _list, _da)
Initialise a cursor that will return only attributes matching the specified fr_dict_attr_t.
fr_pair_t * fr_pair_list_next(fr_pair_list_t const *list, fr_pair_t const *item))
Get the next item in a valuepair list after a specific entry.
void fr_pair_list_append(fr_pair_list_t *dst, fr_pair_list_t *src)
Appends a list of fr_pair_t from a temporary list to a destination list.
fr_pair_t * fr_pair_list_head(fr_pair_list_t const *list)
Get the head of a valuepair list.
int fr_value_box_cmp_op(fr_token_t op, fr_value_box_t const *a, fr_value_box_t const *b)
Compare two attributes using an operator.
#define fr_box_strvalue_buffer(_val)
#define fr_box_strvalue_len(_val, _len)
#define fr_box_strvalue(_val)
uintptr_t fr_value_box_safe_for_t
Escaping that's been applied to a value box.
static TALLOC_CTX * xlat_ctx