27 RCSID(
"$Id: 6f7f593885736337a88c497ade7998c9da10fc47 $")
31 #include <freeradius-devel/util/debug.h>
33 #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,
169 unsigned int entry_cnt;
175 switch (query->
ret) {
181 RDEBUG2(
"Tried to resolve group name(s) to DNs but got no results");
190 if (entry_cnt > group_ctx->
name_cnt) {
191 REDEBUG(
"Number of DNs exceeds number of names, group and/or dn should be more restrictive");
197 if (entry_cnt < group_ctx->name_cnt) {
198 RWDEBUG(
"Got partial mapping of group names (%i) to DNs (%i), membership information may be incomplete",
204 ldap_get_option(query->
ldap_conn->
handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
205 REDEBUG(
"Failed retrieving entry: %s", ldap_err2string(ldap_errno));
214 ldap_get_option(query->
ldap_conn->
handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
215 REDEBUG(
"Retrieving object DN from entry failed: %s", ldap_err2string(ldap_errno));
222 RDEBUG2(
"Got group DN \"%s\"", dn);
257 if (!
inst->group.obj_name_attr) {
258 REDEBUG(
"Told to resolve group DN to name but missing 'group.name_attribute' directive");
262 RDEBUG2(
"Resolving group DN \"%s\" to group name", *group_ctx->
dn);
265 LDAP_SCOPE_BASE, NULL, group_ctx->
attrs, NULL, NULL);
286 struct berval **values = NULL;
291 switch (query->
ret) {
297 REDEBUG(
"Group DN \"%s\" did not resolve to an object", *group_ctx->
dn);
308 ldap_get_option(query->
ldap_conn->
handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
309 REDEBUG(
"Failed retrieving entry: %s", ldap_err2string(ldap_errno));
316 REDEBUG(
"No %s attributes found in object",
inst->group.obj_name_attr);
324 RDEBUG2(
"Group DN \"%s\" resolves to name \"%pV\"", *group_ctx->
dn, &
vp->data);
333 if (values) ldap_value_free_len(values);
355 RDEBUG2(
"Adding cacheable user object memberships");
405 if (*group_ctx->
dn) {
447 LDAPMessage *entry = autz_ctx->
entry;
450 struct berval **values;
454 int is_dn, i,
count, name2dn = 0, dn2name = 0;
464 RDEBUG2(
"No cacheable group memberships found in user object");
468 count = ldap_count_values_len(values);
475 group_ctx->
ttrunk = ttrunk;
493 for (i = 0; (i <
count); i++) {
496 if (
inst->group.cacheable_dn) {
510 REDEBUG(
"Too many groups require name to DN resolution");
512 ldap_value_free_len(values);
520 if (
inst->group.cacheable_name) {
534 REDEBUG(
"Too many groups require DN to name resolution");
542 ldap_value_free_len(values);
550 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);
626 switch (query->
ret) {
632 RDEBUG2(
"No cacheable group memberships found in group objects");
643 ldap_get_option(query->
ldap_conn->
handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
644 REDEBUG(
"Failed retrieving entry: %s", ldap_err2string(ldap_errno));
649 RDEBUG2(
"Adding cacheable group object memberships");
651 if (
inst->group.cacheable_dn) {
654 ldap_get_option(query->
ldap_conn->
handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
655 REDEBUG(
"Retrieving object DN from entry failed: %s", ldap_err2string(ldap_errno));
670 if (
inst->group.cacheable_name) {
671 struct berval **values;
674 if (!values)
continue;
683 ldap_value_free_len(values);
705 if (!
inst->group.obj_membership_filter) {
706 RDEBUG2(
"Skipping caching group objects as directive 'group.membership_filter' is not set");
711 REDEBUG(
"Missing group base_dn");
749 switch (query->
ret) {
753 LDAPMessage *entry = NULL;
805 char const *filters[] = { name_filter,
inst->group.obj_filter,
inst->group.obj_membership_filter };
808 if (!
inst->group.obj_name_attr) {
809 REDEBUG(
"Told to search for group by name, but missing 'group.name_attribute' "
832 snprintf(name_filter,
sizeof(name_filter),
"(%s=%s)",
833 inst->group.obj_name_attr,
xlat_ctx->group->vb_strvalue);
864 if (!
inst->group.obj_name_attr) {
865 REDEBUG(
"Told to resolve group DN to name but missing 'group.name_attribute' directive");
900 LDAP_SCOPE_BASE, NULL,
xlat_ctx->attrs, NULL, NULL);
915 bool value_is_dn =
false;
917 char *value_name = NULL;
926 ldap_get_option(query->
ldap_conn->
handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
927 REDEBUG(
"Failed retrieving entry: %s", ldap_err2string(ldap_errno));
933 RDEBUG2(
"No group membership attribute(s) found in user object");
941 group_ctx->
count = ldap_count_values_len(group_ctx->
values);
948 if (group_ctx->
query) {
950 struct berval **values = NULL;
958 REDEBUG(
"Group DN \"%pV\" did not resolve to an object",
969 REDEBUG(
"Failed retrieving entry: %s", ldap_err2string(ldap_errno));
975 REDEBUG(
"No %s attributes found in object",
inst->group.obj_name_attr);
982 ldap_value_free_len(values);
1002 if (((talloc_array_length(value_name) - 1) == group->vb_length) &&
1003 (memcmp(group->vb_strvalue, value_name, group->vb_length) == 0)) {
1004 RDEBUG2(
"User found in group \"%pV\". Comparison between membership: name "
1005 "(resolved from DN \"%pV\"), check: name", group,
1011 TALLOC_FREE(value_name);
1019 RDEBUG2(
"Processing %s value \"%pV\" as a %s",
inst->group.userobj_membership_attr,
1021 value_is_dn ?
"DN" :
"group name");
1026 if (!
xlat_ctx->group_is_dn && !value_is_dn) {
1027 if ((group->vb_length ==
value->bv_len) &&
1028 (memcmp(
value->bv_val, group->vb_strvalue,
value->bv_len) == 0)) {
1029 RDEBUG2(
"User found in group \"%pV\". Comparison between membership: name, check: name",
1040 if (
xlat_ctx->group_is_dn && value_is_dn) {
1042 RDEBUG2(
"User found in group DN \"%pV\". "
1043 "Comparison between membership: dn, check: dn", group);
1054 if (!value_is_dn &&
xlat_ctx->group_is_dn) {
1060 group_ctx->
lookup_dn = group->vb_strvalue;
1068 if (((talloc_array_length(group_ctx->
group_name) - 1) ==
value->bv_len) &&
1070 RDEBUG2(
"User found in group \"%pV\". Comparison between membership: "
1071 "name, check: name (resolved from DN \"%pV\")",
1083 if (value_is_dn && !
xlat_ctx->group_is_dn) {
1107 if (group_ctx->
values) ldap_value_free_len(group_ctx->
values);
1128 .attrs = {
inst->group.obj_name_attr, NULL }
1131 RDEBUG2(
"Checking user object's %s attributes",
inst->group.userobj_membership_attr);
1174 RDEBUG2(
"User found. Matched cached membership");
1181 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
fr_dcursor_eval_t void const * uctx
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_repeat_set(_request, _repeat)
Set a new repeat function for an existing function frame.
#define unlang_function_push(_request, _func, _repeat, _signal, _sigmask, _top_frame, _uctx)
Push a generic function onto the unlang stack.
static unlang_action_t ldap_cacheable_groupobj_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Process the results of a group object lookup.
static unlang_action_t ldap_cacheable_userobj_store(rlm_rcode_t *p_result, request_t *request, ldap_group_userobj_ctx_t *group_ctx)
Move user object group attributes to the control list.
fr_ldap_query_t * query
Current query performing group lookup.
static char const * null_attrs[]
char const * attrs[2]
For retrieving the group name.
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_cacheable_userobj(rlm_rcode_t *p_result, request_t *request, ldap_autz_ctx_t *autz_ctx, char const *attr)
Convert group membership information into attributes.
void * uctx
Optional context for use in results parsing.
static unlang_action_t ldap_group_dn2name_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Process the results of a group DN -> name lookup.
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(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Process the results of evaluating a user object when checking group membership.
tmpl_t * filter_tmpl
Tmpl to expand into LDAP filter.
static unlang_action_t ldap_group_dn2name_start(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Initiate an LDAP search to turn a group DN into it's name.
char const * attrs[2]
For retrieving the group name.
rlm_ldap_t const * inst
Module instance.
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.
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_groupobj_start(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Initiate an LDAP search for group membership looking at the group objects.
unlang_action_t rlm_ldap_check_userobj_dynamic(rlm_rcode_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.
char const * attrs[2]
For resolving name from DN.
unlang_action_t rlm_ldap_check_cached(rlm_rcode_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.
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(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Initiate resolving a group DN to its name.
unlang_action_t rlm_ldap_cacheable_groupobj(rlm_rcode_t *p_result, request_t *request, ldap_autz_ctx_t *autz_ctx)
Convert group membership information into attributes.
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_group_name2dn_start(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Convert multiple group names into a DNs.
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.
static unlang_action_t ldap_group_name2dn_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Process the results of looking up group DNs from names.
static unlang_action_t ldap_check_userobj_start(UNUSED rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Initiate a user lookup to check membership.
fr_ldap_query_t * query
Current query performing group resolution.
static unlang_action_t ldap_cacheable_userobj_resolve(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Initiate DN to name and name to DN group lookups.
char const * lookup_dn
The DN currently being looked up, when resolving DN to name.
unlang_action_t rlm_ldap_check_groupobj_dynamic(rlm_rcode_t *p_result, request_t *request, ldap_group_xlat_ctx_t *xlat_ctx)
Initiate an LDAP search to determine group membership, querying group objects.
int value_no
The current entry in values being processed.
rlm_ldap_t const * inst
Module instance.
static unlang_action_t ldap_check_groupobj_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Process the results of a group object lookup.
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.
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this 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.
trunk_request_t * treq
Trunk request this query is associated with.
int fr_ldap_box_escape(fr_value_box_t *vb, UNUSED void *uctx)
char * fr_ldap_berval_to_string(TALLOC_CTX *ctx, struct berval const *in)
Convert a berval to a talloced string.
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.
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.
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.
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_MODULE_RCODE(_rcode)
#define RETURN_MODULE_INVALID
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.
#define RETURN_MODULE_NOTFOUND
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::@161 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 check(_handle, _len_p)
#define pair_append_control(_attr, _da)
Allocate and append a fr_pair_t to the control 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.
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.
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.
static char buff[sizeof("18446744073709551615")+3]
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
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_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
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.
fr_pair_t * fr_pair_list_head(fr_pair_list_t const *list)
Get the head of a valuepair list.
#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.
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