The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
groups.c
Go to the documentation of this file.
1/*
2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 6f7f593885736337a88c497ade7998c9da10fc47 $
19 * @file groups.c
20 * @brief LDAP module group functions.
21 *
22 * @author Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23 *
24 * @copyright 2013 Network RADIUS SAS (legal@networkradius.com)
25 * @copyright 2013-2015 The FreeRADIUS Server Project.
26 */
27RCSID("$Id: 6f7f593885736337a88c497ade7998c9da10fc47 $")
28
30
31#include <freeradius-devel/util/debug.h>
32
33#define LOG_PREFIX "rlm_ldap groups"
34
35#include "rlm_ldap.h"
36
37static char const *null_attrs[] = { NULL };
38
39/** Context to use when resolving group membership from the user object.
40 *
41 */
42typedef struct {
43 rlm_ldap_t const *inst; //!< Module instance.
44 fr_value_box_t *base_dn; //!< The base DN to search for groups in.
45 fr_ldap_thread_trunk_t *ttrunk; //!< Trunk on which to perform additional queries.
46 fr_pair_list_t groups; //!< Temporary list to hold pairs.
47 TALLOC_CTX *list_ctx; //!< In which to allocate pairs.
48 char *group_name[LDAP_MAX_CACHEABLE + 1]; //!< List of group names which need resolving.
49 unsigned int name_cnt; //!< How many names need resolving.
50 char *group_dn[LDAP_MAX_CACHEABLE + 1]; //!< List of group DNs which need resolving.
51 char **dn; //!< Current DN being resolved.
52 char const *attrs[2]; //!< For resolving name from DN.
53 fr_ldap_query_t *query; //!< Current query performing group resolution.
55
56/** Context to use when looking up group membership using group objects.
57 *
58 */
59typedef struct {
60 rlm_ldap_t const *inst; //!< Module instance.
61 fr_value_box_t *base_dn; //!< The base DN to search for groups in.
62 fr_ldap_thread_trunk_t *ttrunk; //!< Trunk on which to perform additional queries.
63 tmpl_t *filter_tmpl; //!< Tmpl to expand into LDAP filter.
64 fr_value_box_list_t expanded_filter; //!< Values produced by expanding filter xlat.
65 char const *attrs[2]; //!< For retrieving the group name.
66 fr_ldap_query_t *query; //!< Current query performing group lookup.
67 void *uctx; //!< Optional context for use in results parsing.
69
70/** Context to use when evaluating group membership from the user object in an xlat
71 *
72 */
73typedef struct {
74 ldap_group_xlat_ctx_t *xlat_ctx; //!< Xlat context being evaluated.
75 char const *attrs[2]; //!< For retrieving the group name.
76 struct berval **values; //!< Values of the membership attribute to check.
77 int count; //!< How many entries there are in values.
78 int value_no; //!< The current entry in values being processed.
79 char const *lookup_dn; //!< The DN currently being looked up, when resolving DN to name.
80 char *group_name; //!< Result of resolving the provided group DN as to a name.
81 fr_ldap_query_t *query; //!< Current query doing a DN to name resolution.
82 bool resolving_value; //!< Is the current query resolving a DN from values.
84
85/** Cancel a pending group lookup query
86 *
87 */
88static void ldap_group_userobj_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
89{
90 ldap_group_userobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_ctx_t);
91
92 /*
93 * If the query is not in flight, just return.
94 */
95 if (!group_ctx->query || !(group_ctx->query->treq)) return;
96
98}
99
100/** Convert multiple group names into a DNs
101 *
102 * Given an array of group names, builds a filter matching all names, then retrieves all group objects
103 * and stores the DN associated with each group object.
104 *
105 * @param[out] p_result The result of trying to resolve a group name to a dn.
106 * @param[out] priority Unused
107 * @param[in] request Current request.
108 * @param[in] uctx Group lookup context.
109 * @return One of the RLM_MODULE_* values.
110 */
111static unlang_action_t ldap_group_name2dn_start(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request,
112 void *uctx)
113{
114 ldap_group_userobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_ctx_t);
115 rlm_ldap_t const *inst = group_ctx->inst;
116 char **name = group_ctx->group_name;
118 char *filter;
119
120 if (!inst->group.obj_name_attr) {
121 REDEBUG("Told to convert group names to DNs but missing 'group.name_attribute' directive");
123 }
124 if (group_ctx->base_dn->type != FR_TYPE_STRING) {
125 REDEBUG("Missing group base_dn");
127 }
128
129 RDEBUG2("Converting group name(s) to group DN(s)");
130
131 /*
132 * It'll probably only save a few ms in network latency, but it means we can send a query
133 * for the entire group list at once.
134 */
135 filter = talloc_typed_asprintf(group_ctx, "%s%s%s",
136 inst->group.obj_filter ? "(&" : "",
137 inst->group.obj_filter ? inst->group.obj_filter : "",
138 group_ctx->group_name[0] && group_ctx->group_name[1] ? "(|" : "");
139 while (*name) {
140 fr_ldap_uri_escape_func(request, buffer, sizeof(buffer), *name++, NULL);
141 filter = talloc_asprintf_append_buffer(filter, "(%s=%s)", inst->group.obj_name_attr, buffer);
142
143 group_ctx->name_cnt++;
144 }
145 filter = talloc_asprintf_append_buffer(filter, "%s%s",
146 inst->group.obj_filter ? ")" : "",
147 group_ctx->group_name[0] && group_ctx->group_name[1] ? ")" : "");
148
149 return fr_ldap_trunk_search(group_ctx, &group_ctx->query, request, group_ctx->ttrunk,
150 group_ctx->base_dn->vb_strvalue, inst->group.obj_scope, filter,
151 null_attrs, NULL, NULL);
152}
153
154/** Process the results of looking up group DNs from names
155 *
156 * @param[out] p_result The result of trying to resolve a group name to a dn.
157 * @param[out] priority Unused
158 * @param[in] request Current request.
159 * @param[in] uctx Group lookup context.
160 * @return One of the RLM_MODULE_* values.
161 */
162static unlang_action_t ldap_group_name2dn_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request,
163 void *uctx)
164{
165 ldap_group_userobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_ctx_t);
166 fr_ldap_query_t *query = talloc_get_type_abort(group_ctx->query, fr_ldap_query_t);
167 rlm_ldap_t const *inst = group_ctx->inst;
169 unsigned int entry_cnt;
170 LDAPMessage *entry;
171 int ldap_errno;
172 char *dn;
173 fr_pair_t *vp;
174
175 switch (query->ret) {
177 break;
178
181 RDEBUG2("Tried to resolve group name(s) to DNs but got no results");
182 goto finish;
183
184 default:
185 rcode = RLM_MODULE_FAIL;
186 goto finish;
187 }
188
189 entry_cnt = ldap_count_entries(query->ldap_conn->handle, query->result);
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");
192 rcode = RLM_MODULE_INVALID;
193
194 goto finish;
195 }
196
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",
199 group_ctx->name_cnt, entry_cnt);
200 }
201
202 entry = ldap_first_entry(query->ldap_conn->handle, query->result);
203 if (!entry) {
204 ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
205 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
206
207 rcode = RLM_MODULE_FAIL;
208 goto finish;
209 }
210
211 do {
212 dn = ldap_get_dn(query->ldap_conn->handle, entry);
213 if (!dn) {
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));
216
217 rcode = RLM_MODULE_FAIL;
218 goto finish;
219 }
221
222 RDEBUG2("Got group DN \"%s\"", dn);
223 MEM(vp = fr_pair_afrom_da(group_ctx->list_ctx, inst->group.cache_da));
224 fr_pair_value_bstrndup(vp, dn, strlen(dn), true);
225 fr_pair_append(&group_ctx->groups, vp);
226 ldap_memfree(dn);
227 } while((entry = ldap_next_entry(query->ldap_conn->handle, entry)));
228
229finish:
230 /*
231 * Remove pointer to group name to resolve so we don't
232 * try to do it again
233 */
234 *group_ctx->group_name = NULL;
235 talloc_free(group_ctx->query);
236
237 RETURN_MODULE_RCODE(rcode);
238}
239
240/** Initiate an LDAP search to turn a group DN into it's name
241 *
242 * Unlike the inverse conversion of a name to a DN, most LDAP directories don't allow filtering by DN,
243 * so we need to search for each DN individually.
244 *
245 * @param[out] p_result The result of trying to resolve a dn to a group name.
246 * @param[in] priority unused.
247 * @param[in] request Current request.
248 * @param[in] uctx The group resolution context.
249 * @return One of the RLM_MODULE_* values.
250 */
251static unlang_action_t ldap_group_dn2name_start(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request,
252 void *uctx)
253{
254 ldap_group_userobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_ctx_t);
255 rlm_ldap_t const *inst = group_ctx->inst;
256
257 if (!inst->group.obj_name_attr) {
258 REDEBUG("Told to resolve group DN to name but missing 'group.name_attribute' directive");
260 }
261
262 RDEBUG2("Resolving group DN \"%s\" to group name", *group_ctx->dn);
263
264 return fr_ldap_trunk_search(group_ctx, &group_ctx->query, request, group_ctx->ttrunk, *group_ctx->dn,
265 LDAP_SCOPE_BASE, NULL, group_ctx->attrs, NULL, NULL);
266}
267
268/** Process the results of a group DN -> name lookup.
269 *
270 * The retrieved value is added as a value pair to the
271 * temporary list in the group resolution context.
272 *
273 * @param[out] p_result The result of trying to resolve a dn to a group name.
274 * @param[in] priority unused.
275 * @param[in] request Current request.
276 * @param[in] uctx The group resolution context.
277 * @return One of the RLM_MODULE_* values.
278 */
279static unlang_action_t ldap_group_dn2name_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request,
280 void *uctx)
281{
282 ldap_group_userobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_ctx_t);
283 fr_ldap_query_t *query = talloc_get_type_abort(group_ctx->query, fr_ldap_query_t);
284 rlm_ldap_t const *inst = group_ctx->inst;
285 LDAPMessage *entry;
286 struct berval **values = NULL;
287 int ldap_errno;
289 fr_pair_t *vp;
290
291 switch (query->ret) {
293 break;
294
297 REDEBUG("Group DN \"%s\" did not resolve to an object", *group_ctx->dn);
298 rcode = (inst->group.allow_dangling_refs ? RLM_MODULE_NOOP : RLM_MODULE_INVALID);
299 goto finish;
300
301 default:
302 rcode = RLM_MODULE_FAIL;
303 goto finish;
304 }
305
306 entry = ldap_first_entry(query->ldap_conn->handle, query->result);
307 if (!entry) {
308 ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
309 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
310 rcode = RLM_MODULE_INVALID;
311 goto finish;
312 }
313
314 values = ldap_get_values_len(query->ldap_conn->handle, entry, inst->group.obj_name_attr);
315 if (!values) {
316 REDEBUG("No %s attributes found in object", inst->group.obj_name_attr);
317 rcode = RLM_MODULE_INVALID;
318 goto finish;
319 }
320
321 MEM(vp = fr_pair_afrom_da(group_ctx->list_ctx, inst->group.cache_da));
322 fr_pair_value_bstrndup(vp, values[0]->bv_val, values[0]->bv_len, true);
323 fr_pair_append(&group_ctx->groups, vp);
324 RDEBUG2("Group DN \"%s\" resolves to name \"%pV\"", *group_ctx->dn, &vp->data);
325
326finish:
327 /*
328 * Walk the pointer to the DN being resolved forward
329 * ready for the next resolution.
330 */
331 group_ctx->dn++;
332
333 if (values) ldap_value_free_len(values);
334 talloc_free(query);
335
336 RETURN_MODULE_RCODE(rcode);
337}
338
339/** Move user object group attributes to the control list
340 *
341 * @param p_result The result of adding user object group attributes
342 * @param request Current request.
343 * @param group_ctx Context used to evaluate group attributes
344 * @return RLM_MODULE_OK
345 */
347 ldap_group_userobj_ctx_t *group_ctx)
348{
349 fr_pair_t *vp;
350 fr_pair_list_t *list;
351
352 list = tmpl_list_head(request, request_attr_control);
353 fr_assert(list != NULL);
354
355 RDEBUG2("Adding cacheable user object memberships");
356 RINDENT();
357 if (RDEBUG_ENABLED) {
358 for (vp = fr_pair_list_head(&group_ctx->groups);
359 vp;
360 vp = fr_pair_list_next(&group_ctx->groups, vp)) {
361 RDEBUG2("&control.%s += \"%pV\"", group_ctx->inst->group.cache_da->name, &vp->data);
362 }
363 }
364
365 fr_pair_list_append(list, &group_ctx->groups);
366 REXDENT();
367
368 talloc_free(group_ctx);
370}
371
372/** Initiate DN to name and name to DN group lookups
373 *
374 * Called repeatedly until there are no more lookups to perform
375 * or an unresolved lookup causes the module to fail.
376 *
377 * @param p_result The result of the previous expansion.
378 * @param priority unused.
379 * @param request Current request.
380 * @param uctx The group context being processed.
381 * @return One of the RLM_MODULE_* values.
382 */
384 request_t *request, void *uctx)
385{
386 ldap_group_userobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_ctx_t);
387
388 /*
389 * If we've previously failed to expand, fail the group section
390 */
391 switch (*p_result) {
392 case RLM_MODULE_FAIL:
394 talloc_free(group_ctx);
396 default:
397 break;
398 }
399
400 /*
401 * Are there any DN to resolve to names?
402 * These are resolved one at a time as most directories don't allow for
403 * filters on the DN.
404 */
405 if (*group_ctx->dn) {
409 UNLANG_SUB_FRAME, group_ctx) < 0) RETURN_MODULE_FAIL;
411 }
412
413 /*
414 * Are there any names to resolve to DN?
415 */
416 if (*group_ctx->group_name) {
420 UNLANG_SUB_FRAME, group_ctx) < 0) RETURN_MODULE_FAIL;
422 }
423
424 /*
425 * Nothing left to resolve, move the resulting attributes to
426 * the control list.
427 */
428 return ldap_cacheable_userobj_store(p_result, request, group_ctx);
429}
430
431/** Convert group membership information into attributes
432 *
433 * This may just be able to parse attribute values in the user object
434 * or it may need to yield to other LDAP searches depending on what was
435 * returned and what is set to be cached.
436 *
437 * @param[out] p_result The result of trying to resolve a dn to a group name.
438 * @param[in] request Current request.
439 * @param[in] autz_ctx LDAP authorization context being processed.
440 * @param[in] attr membership attribute to look for in the entry.
441 * @return One of the RLM_MODULE_* values.
442 */
444 char const *attr)
445{
446 rlm_ldap_t const *inst = autz_ctx->inst;
447 LDAPMessage *entry = autz_ctx->entry;
448 fr_ldap_thread_trunk_t *ttrunk = autz_ctx->ttrunk;
449 ldap_group_userobj_ctx_t *group_ctx;
450 struct berval **values;
451 char **name_p;
452 char **dn_p;
453 fr_pair_t *vp;
454 int is_dn, i, count, name2dn = 0, dn2name = 0;
455
456 fr_assert(entry);
457 fr_assert(attr);
458
459 /*
460 * Parse the membership information we got in the initial user query.
461 */
462 values = ldap_get_values_len(fr_ldap_handle_thread_local(), entry, attr);
463 if (!values) {
464 RDEBUG2("No cacheable group memberships found in user object");
465
467 }
468 count = ldap_count_values_len(values);
469
470 /*
471 * Set up context for managing group membership attribute resolution.
472 */
473 MEM(group_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), ldap_group_userobj_ctx_t));
474 group_ctx->inst = inst;
475 group_ctx->ttrunk = ttrunk;
476 group_ctx->base_dn = &autz_ctx->call_env->group_base;
477 group_ctx->list_ctx = tmpl_list_ctx(request, request_attr_control);
478 fr_assert(group_ctx->list_ctx != NULL);
479
480 /*
481 * Set up pointers to entries in arrays of names / DNs to resolve.
482 */
483 name_p = group_ctx->group_name;
484 group_ctx->dn = dn_p = group_ctx->group_dn;
485
486 /*
487 * Temporary list to hold new group VPs, will be merged
488 * once all group info has been gathered/resolved
489 * successfully.
490 */
491 fr_pair_list_init(&group_ctx->groups);
492
493 for (i = 0; (i < count); i++) {
494 is_dn = fr_ldap_util_is_dn(values[i]->bv_val, values[i]->bv_len);
495
496 if (inst->group.cacheable_dn) {
497 /*
498 * The easy case, we're caching DNs and we got a DN.
499 */
500 if (is_dn) {
501 MEM(vp = fr_pair_afrom_da(group_ctx->list_ctx, inst->group.cache_da));
502 fr_pair_value_bstrndup(vp, values[i]->bv_val, values[i]->bv_len, true);
503 fr_pair_append(&group_ctx->groups, vp);
504 /*
505 * We were told to cache DNs but we got a name, we now need to resolve
506 * this to a DN. Store all the group names in an array so we can do one query.
507 */
508 } else {
509 if (++name2dn > LDAP_MAX_CACHEABLE) {
510 REDEBUG("Too many groups require name to DN resolution");
511 invalid:
512 ldap_value_free_len(values);
513 talloc_free(group_ctx);
515 }
516 *name_p++ = fr_ldap_berval_to_string(group_ctx, values[i]);
517 }
518 }
519
520 if (inst->group.cacheable_name) {
521 /*
522 * The easy case, we're caching names and we got a name.
523 */
524 if (!is_dn) {
525 MEM(vp = fr_pair_afrom_da(group_ctx->list_ctx, inst->group.cache_da));
526 fr_pair_value_bstrndup(vp, values[i]->bv_val, values[i]->bv_len, true);
527 fr_pair_append(&group_ctx->groups, vp);
528 /*
529 * We were told to cache names but we got a DN, we now need to resolve
530 * this to a name. Store group DNs which need resolving to names.
531 */
532 } else {
533 if (++dn2name > LDAP_MAX_CACHEABLE) {
534 REDEBUG("Too many groups require DN to name resolution");
535 goto invalid;
536 }
537 *dn_p++ = fr_ldap_berval_to_string(group_ctx, values[i]);
538 }
539 }
540 }
541
542 ldap_value_free_len(values);
543
544 /*
545 * We either have group names which need converting to DNs or
546 * DNs which need resolving to names. Push a function which will
547 * do the resolution.
548 */
549 if ((name_p != group_ctx->group_name) || (dn_p != group_ctx->group_dn)) {
550 group_ctx->attrs[0] = inst->group.obj_name_attr;
552 ~FR_SIGNAL_CANCEL, UNLANG_SUB_FRAME, group_ctx) < 0) {
553 talloc_free(group_ctx);
555 }
557 }
558
559 /*
560 * No additional queries needed, just process the context to
561 * move any generated pairs into the correct list.
562 */
563 return ldap_cacheable_userobj_store(p_result, request, group_ctx);
564}
565
566/** Initiate an LDAP search for group membership looking at the group objects
567 *
568 * @param[out] p_result Result of submitting LDAP search
569 * @param[out] priority Unused.
570 * @param[in] request Current request.
571 * @param[in] uctx Group lookup context.
572 * @return One of the RLM_MODULE_* values.
573 */
575 void *uctx)
576{
577 ldap_group_groupobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_groupobj_ctx_t);
578 rlm_ldap_t const *inst = group_ctx->inst;
579 fr_value_box_t *filter;
580
581 filter = fr_value_box_list_head(&group_ctx->expanded_filter);
582
583 if (filter->type != FR_TYPE_STRING) RETURN_MODULE_FAIL;
584
585 group_ctx->attrs[0] = inst->group.obj_name_attr;
586 return fr_ldap_trunk_search(group_ctx, &group_ctx->query, request, group_ctx->ttrunk,
587 group_ctx->base_dn->vb_strvalue, inst->group.obj_scope,
588 filter->vb_strvalue, group_ctx->attrs, NULL, NULL);
589}
590
591/** Cancel a pending group object lookup.
592 *
593 */
594static void ldap_group_groupobj_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
595{
596 ldap_group_groupobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_groupobj_ctx_t);
597
598 /*
599 * If the query is not in flight, just return
600 */
601 if (!group_ctx->query || !group_ctx->query->treq) return;
602
604}
605
606/** Process the results of a group object lookup.
607 *
608 * @param[out] p_result Result of processing group lookup.
609 * @param[out] priority Unused.
610 * @param[in] request Current request.
611 * @param[in] uctx Group lookup context.
612 * @return One of the RLM_MODULE_* values.
613 */
615 void *uctx)
616{
617 ldap_group_groupobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_groupobj_ctx_t);
618 rlm_ldap_t const *inst = group_ctx->inst;
619 fr_ldap_query_t *query = group_ctx->query;
621 LDAPMessage *entry;
622 int ldap_errno;
623 char *dn;
624 fr_pair_t *vp;
625
626 switch (query->ret) {
627 case LDAP_SUCCESS:
628 break;
629
632 RDEBUG2("No cacheable group memberships found in group objects");
633 rcode = RLM_MODULE_NOTFOUND;
634 goto finish;
635
636 default:
637 rcode = RLM_MODULE_FAIL;
638 goto finish;
639 }
640
641 entry = ldap_first_entry(query->ldap_conn->handle, query->result);
642 if (!entry) {
643 ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
644 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
645
646 goto finish;
647 }
648
649 RDEBUG2("Adding cacheable group object memberships");
650 do {
651 if (inst->group.cacheable_dn) {
652 dn = ldap_get_dn(query->ldap_conn->handle, entry);
653 if (!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));
656
657 goto finish;
658 }
660
661 MEM(pair_append_control(&vp, inst->group.cache_da) == 0);
662 fr_pair_value_strdup(vp, dn, false);
663
664 RINDENT();
665 RDEBUG2("&control.%pP", vp);
666 REXDENT();
667 ldap_memfree(dn);
668 }
669
670 if (inst->group.cacheable_name) {
671 struct berval **values;
672
673 values = ldap_get_values_len(query->ldap_conn->handle, entry, inst->group.obj_name_attr);
674 if (!values) continue;
675
676 MEM(pair_append_control(&vp, inst->group.cache_da) == 0);
677 fr_pair_value_bstrndup(vp, values[0]->bv_val, values[0]->bv_len, true);
678
679 RINDENT();
680 RDEBUG2("&control.%pP", vp);
681 REXDENT();
682
683 ldap_value_free_len(values);
684 }
685 } while ((entry = ldap_next_entry(query->ldap_conn->handle, entry)));
686
687finish:
688 talloc_free(group_ctx);
689
690 RETURN_MODULE_RCODE(rcode);
691}
692
693/** Convert group membership information into attributes
694 *
695 * @param[out] p_result The result of trying to resolve a dn to a group name.
696 * @param[in] request Current request.
697 * @param[in] autz_ctx Authentication context being processed.
698 * @return One of the RLM_MODULE_* values.
699 */
701{
702 rlm_ldap_t const *inst = autz_ctx->inst;
703 ldap_group_groupobj_ctx_t *group_ctx;
704
705 if (!inst->group.obj_membership_filter) {
706 RDEBUG2("Skipping caching group objects as directive 'group.membership_filter' is not set");
708 }
709
710 if (autz_ctx->call_env->group_base.type != FR_TYPE_STRING) {
711 REDEBUG("Missing group base_dn");
713 }
714
715 MEM(group_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), ldap_group_groupobj_ctx_t));
716 group_ctx->inst = inst;
717 group_ctx->ttrunk = autz_ctx->ttrunk;
718 group_ctx->base_dn = &autz_ctx->call_env->group_base;
719 fr_value_box_list_init(&group_ctx->expanded_filter);
720
723 error:
724 talloc_free(group_ctx);
726 }
727
728 if (unlang_tmpl_push(group_ctx, &group_ctx->expanded_filter, request, autz_ctx->call_env->group_filter, NULL) < 0) goto error;
729
731}
732
733/** Process the results of a group object lookup.
734 *
735 * @param[out] p_result Result of processing group lookup.
736 * @param[out] priority Unused.
737 * @param[in] request Current request.
738 * @param[in] uctx Group lookup context.
739 * @return One of the RLM_MODULE_* values.
740 */
742 void *uctx)
743{
744 ldap_group_groupobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_groupobj_ctx_t);
745 ldap_group_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(group_ctx->uctx, ldap_group_xlat_ctx_t);
746 fr_ldap_query_t *query = group_ctx->query;
748
749 switch (query->ret) {
750 case LDAP_SUCCESS:
751 xlat_ctx->found = true;
752 if (RDEBUG_ENABLED2) {
753 LDAPMessage *entry = NULL;
754 char *dn = NULL;
755 entry = ldap_first_entry(query->ldap_conn->handle, query->result);
756 if (entry) {
757 dn = ldap_get_dn(query->ldap_conn->handle, entry);
758 RDEBUG2("User found in group object \"%pV\"", fr_box_strvalue(dn));
759 ldap_memfree(dn);
760 }
761 }
762 break;
763
766 rcode = RLM_MODULE_NOTFOUND;
767 break;
768
769 default:
770 rcode = RLM_MODULE_FAIL;
771 break;
772 }
773
774 talloc_free(group_ctx);
775 RETURN_MODULE_RCODE(rcode);
776}
777
778/** Initiate an LDAP search to determine group membership, querying group objects
779 *
780 * Used by LDAP group membership xlat
781 *
782 * @param p_result Current module result code.
783 * @param request Current request.
784 * @param xlat_ctx xlat context being processed.
785 */
788{
789 rlm_ldap_t const *inst = xlat_ctx->inst;
790 ldap_group_groupobj_ctx_t *group_ctx;
791
793 *group_ctx = (ldap_group_groupobj_ctx_t) {
794 .inst = inst,
795 .ttrunk = xlat_ctx->ttrunk,
796 .uctx = xlat_ctx
797 };
798 fr_value_box_list_init(&group_ctx->expanded_filter);
799
800 if (fr_ldap_util_is_dn(xlat_ctx->group->vb_strvalue, xlat_ctx->group->vb_length)) {
801 group_ctx->filter_tmpl = xlat_ctx->env_data->group_filter;
802 group_ctx->base_dn = xlat_ctx->group;
803 } else {
804 char name_filter[LDAP_MAX_FILTER_STR_LEN];
805 char const *filters[] = { name_filter, inst->group.obj_filter, inst->group.obj_membership_filter };
806 tmpl_rules_t t_rules;
807
808 if (!inst->group.obj_name_attr) {
809 REDEBUG("Told to search for group by name, but missing 'group.name_attribute' "
810 "directive");
811 invalid:
812 talloc_free(group_ctx);
814 }
815
816 t_rules = (tmpl_rules_t){
817 .attr = {
818 .dict_def = request->dict,
819 .list_def = request_attr_request,
820 },
821 .xlat = {
822 .runtime_el = unlang_interpret_event_list(request),
823 },
824 .at_runtime = true,
825 .escape.func = fr_ldap_box_escape,
827 .escape.mode = TMPL_ESCAPE_PRE_CONCAT,
828 .literals_safe_for = (fr_value_box_safe_for_t)fr_ldap_box_escape,
829 .cast = FR_TYPE_STRING,
830 };
831
832 snprintf(name_filter, sizeof(name_filter), "(%s=%s)",
833 inst->group.obj_name_attr, xlat_ctx->group->vb_strvalue);
834
835 if (fr_ldap_filter_to_tmpl(group_ctx, &t_rules, filters, NUM_ELEMENTS(filters),
836 &group_ctx->filter_tmpl) < 0) goto invalid;
837
838 fr_assert(xlat_ctx->env_data);
839 group_ctx->base_dn = &xlat_ctx->env_data->group_base;
840 }
841
844 UNLANG_SUB_FRAME, group_ctx) < 0) {
845 error:
846 talloc_free(group_ctx);
848 }
849
850 if (unlang_tmpl_push(group_ctx, &group_ctx->expanded_filter, request, group_ctx->filter_tmpl, NULL) < 0) goto error;
851
853}
854
855/** Initiate resolving a group DN to its name
856 *
857 */
858static unlang_action_t ldap_dn2name_start (rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
859{
860 ldap_group_userobj_dyn_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_dyn_ctx_t);
862 rlm_ldap_t const *inst = xlat_ctx->inst;
863
864 if (!inst->group.obj_name_attr) {
865 REDEBUG("Told to resolve group DN to name but missing 'group.name_attribute' directive");
867 }
868
869 RDEBUG2("Resolving group DN \"%pV\" to group name", fr_box_strvalue_buffer(group_ctx->lookup_dn));
870
871 return fr_ldap_trunk_search(group_ctx, &group_ctx->query, request, xlat_ctx->ttrunk,
872 group_ctx->lookup_dn, LDAP_SCOPE_BASE, NULL, group_ctx->attrs,
873 NULL, NULL);
874}
875
876/** Cancel an in-progress DN to name lookup.
877 *
878 */
879static void ldap_dn2name_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
880{
881 ldap_group_userobj_dyn_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_dyn_ctx_t);
882
883 if (!group_ctx->query || !group_ctx->query->treq) return;
884
886}
887
888/** Initiate a user lookup to check membership.
889 *
890 * Used when the user's DN is already known but cached group membership has not been stored
891 *
892 */
894 request_t *request, void *uctx)
895{
896 ldap_group_userobj_dyn_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_dyn_ctx_t);
897 ldap_group_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(group_ctx->xlat_ctx, ldap_group_xlat_ctx_t);
898
899 return fr_ldap_trunk_search(xlat_ctx, &xlat_ctx->query, request, xlat_ctx->ttrunk, xlat_ctx->dn,
900 LDAP_SCOPE_BASE, NULL, xlat_ctx->attrs, NULL, NULL);
901}
902
903/** Process the results of evaluating a user object when checking group membership
904 *
905 */
907 request_t *request, void *uctx)
908{
909 ldap_group_userobj_dyn_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_dyn_ctx_t);
910 ldap_group_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(group_ctx->xlat_ctx, ldap_group_xlat_ctx_t);
911 rlm_ldap_t const *inst = xlat_ctx->inst;
912 fr_ldap_query_t *query = xlat_ctx->query;
913 LDAPMessage *entry;
914 int ldap_errno;
915 bool value_is_dn = false;
916 fr_value_box_t *group = xlat_ctx->group;
917 char *value_name = NULL;
918
919 /*
920 * If group_ctx->values is not populated, this is the first call
921 * - extract the returned values if any.
922 */
923 if (!group_ctx->values) {
924 entry = ldap_first_entry(query->ldap_conn->handle, query->result);
925 if (!entry) {
926 ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
927 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
929 }
930
931 group_ctx->values = ldap_get_values_len(query->ldap_conn->handle, entry, inst->group.userobj_membership_attr);
932 if (!group_ctx->values) {
933 RDEBUG2("No group membership attribute(s) found in user object");
935 }
936
937 /*
938 * To avoid re-assessing after each call out to do a DN -> name
939 * lookup, cache this.
940 */
941 group_ctx->count = ldap_count_values_len(group_ctx->values);
942 }
943
944 /*
945 * Following a call out to do a DN -> name lookup, group_ctx->query will be
946 * populated - process the results.
947 */
948 if (group_ctx->query) {
949 char *buff;
950 struct berval **values = NULL;
951
952 switch (group_ctx->query->ret) {
954 break;
955
958 REDEBUG("Group DN \"%pV\" did not resolve to an object",
961
962 default:
964 }
965
966 entry = ldap_first_entry(group_ctx->query->ldap_conn->handle, group_ctx->query->result);
967 if (!entry) {
968 ldap_get_option(group_ctx->query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
969 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
971 }
972
973 values = ldap_get_values_len(group_ctx->query->ldap_conn->handle, entry, inst->group.obj_name_attr);
974 if (!values) {
975 REDEBUG("No %s attributes found in object", inst->group.obj_name_attr);
977 }
978
979 MEM(buff = talloc_bstrndup(group_ctx, values[0]->bv_val, values[0]->bv_len));
980 RDEBUG2("Group DN \"%pV\" resolves to name \"%pV\"", fr_box_strvalue_buffer(group_ctx->lookup_dn),
981 fr_box_strvalue_len(values[0]->bv_val, values[0]->bv_len));
982 ldap_value_free_len(values);
983
984 if (group_ctx->resolving_value) {
985 value_name = buff;
986 } else {
987 group_ctx->group_name = buff;
988 }
989 }
990
991 /*
992 * Loop over the list of groups the user is a member of, looking for a match.
993 */
994 while (group_ctx->value_no < group_ctx->count) {
995 struct berval *value = group_ctx->values[group_ctx->value_no];
996
997 /*
998 * We have come back from resolving a membership DN to its name,
999 * compare to the provided name.
1000 */
1001 if (value_name && group_ctx->resolving_value) {
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,
1006 fr_box_strvalue_buffer(group_ctx->lookup_dn));
1007 talloc_free(value_name);
1008 goto found;
1009 }
1010 talloc_const_free(group_ctx->lookup_dn);
1011 TALLOC_FREE(value_name);
1012 group_ctx->resolving_value = false;
1013 group_ctx->value_no++;
1014 continue;
1015 }
1016
1017 value_is_dn = fr_ldap_util_is_dn(value->bv_val, value->bv_len);
1018
1019 RDEBUG2("Processing %s value \"%pV\" as a %s", inst->group.userobj_membership_attr,
1020 fr_box_strvalue_len(value->bv_val, value->bv_len),
1021 value_is_dn ? "DN" : "group name");
1022
1023 /*
1024 * Both literal group names, do case sensitive comparison
1025 */
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",
1030 group);
1031 goto found;
1032 }
1033 group_ctx->value_no++;
1034 continue;
1035 }
1036
1037 /*
1038 * Both DNs, do case insensitive, binary safe comparison
1039 */
1040 if (xlat_ctx->group_is_dn && value_is_dn) {
1041 if (fr_ldap_berval_strncasecmp(value, group->vb_strvalue, group->vb_length) == 0) {
1042 RDEBUG2("User found in group DN \"%pV\". "
1043 "Comparison between membership: dn, check: dn", group);
1044 goto found;
1045 }
1046 group_ctx->value_no++;
1047 continue;
1048 }
1049
1050 /*
1051 * If the value is not a DN, and the name we were given is a dn
1052 * convert the value to a DN and do a comparison.
1053 */
1054 if (!value_is_dn && xlat_ctx->group_is_dn) {
1055 /*
1056 * So we only do the DN -> name lookup once, regardless of how many
1057 * group values we have to check, the resolved name is put in group_ctx->group_name
1058 */
1059 if (!group_ctx->group_name) {
1060 group_ctx->lookup_dn = group->vb_strvalue;
1061
1063
1065 ~FR_SIGNAL_CANCEL, UNLANG_SUB_FRAME, group_ctx);
1066 }
1067
1068 if (((talloc_array_length(group_ctx->group_name) - 1) == value->bv_len) &&
1069 (memcmp(value->bv_val, group_ctx->group_name, value->bv_len) == 0)) {
1070 RDEBUG2("User found in group \"%pV\". Comparison between membership: "
1071 "name, check: name (resolved from DN \"%pV\")",
1072 fr_box_strvalue_len(value->bv_val, value->bv_len), group);
1073 goto found;
1074 }
1075 group_ctx->value_no++;
1076 continue;
1077 }
1078
1079 /*
1080 * We have a value which is a DN, and a check item which specifies the name of a group,
1081 * convert the value to a name so we can do a comparison.
1082 */
1083 if (value_is_dn && !xlat_ctx->group_is_dn) {
1084 group_ctx->lookup_dn = fr_ldap_berval_to_string(group_ctx, value);
1085 group_ctx->resolving_value = true;
1086
1088
1090 ~FR_SIGNAL_CANCEL, UNLANG_SUB_FRAME, group_ctx);
1091 }
1092
1093 fr_assert(0);
1094 }
1096
1097found:
1098 xlat_ctx->found = true;
1100}
1101
1102/** Ensure retrieved LDAP values are cleared up
1103 *
1104 */
1106{
1107 if (group_ctx->values) ldap_value_free_len(group_ctx->values);
1108 return 0;
1109}
1110
1111/** Query the LDAP directory to check if a user object is a member of a group
1112 *
1113 * @param[out] p_result Result of calling the module.
1114 * @param[in] request Current request.
1115 * @param[in] xlat_ctx Context of the xlat being evaluated.
1116 */
1119{
1120 rlm_ldap_t const *inst = xlat_ctx->inst;
1122
1124 talloc_set_destructor(group_ctx, userobj_dyn_free);
1125
1126 *group_ctx = (ldap_group_userobj_dyn_ctx_t) {
1127 .xlat_ctx = xlat_ctx,
1128 .attrs = { inst->group.obj_name_attr, NULL }
1129 };
1130
1131 RDEBUG2("Checking user object's %s attributes", inst->group.userobj_membership_attr);
1132
1133 /*
1134 * If a previous query was required to find the user DN, that will have
1135 * retrieved the user object membership attribute and the resulting values
1136 * can be checked.
1137 * If not then a query is needed to retrieve the user object.
1138 */
1140 ldap_group_userobj_cancel, ~FR_SIGNAL_CANCEL, UNLANG_SUB_FRAME, group_ctx) < 0) {
1141 talloc_free(group_ctx);
1143 }
1144
1146}
1147
1148/** Check group membership attributes to see if a user is a member.
1149 *
1150 * @param[out] p_result Result of calling the module.
1151 * @param[in] inst rlm_ldap configuration.
1152 * @param[in] request Current request.
1153 * @param[in] check vb containing the group value (name or dn).
1154 */
1156 rlm_ldap_t const *inst, request_t *request, fr_value_box_t const *check)
1157{
1158 fr_pair_t *vp;
1159 int ret;
1160 fr_dcursor_t cursor;
1161
1162 /*
1163 * We return RLM_MODULE_INVALID here as an indication
1164 * the caller should try a dynamic group lookup instead.
1165 */
1166 vp = fr_pair_dcursor_by_da_init(&cursor, &request->control_pairs, inst->group.cache_da);
1168
1169 for (vp = fr_dcursor_current(&cursor);
1170 vp;
1171 vp = fr_dcursor_next(&cursor)) {
1172 ret = fr_value_box_cmp_op(T_OP_CMP_EQ, &vp->data, check);
1173 if (ret == 1) {
1174 RDEBUG2("User found. Matched cached membership");
1176 }
1177
1178 if (ret < -1) RETURN_MODULE_FAIL;
1179 }
1180
1181 RDEBUG2("Cached membership not found");
1182
1184}
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
@ UNLANG_ACTION_PUSHED_CHILD
unlang_t pushed a new child onto the stack, execute it instead of continuing.
Definition action.h:39
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
Definition action.h:37
static int const char char buffer[256]
Definition acutest.h:576
#define USES_APPLE_DEPRECATED_API
Definition build.h:470
#define RCSID(id)
Definition build.h:483
#define UNUSED
Definition build.h:315
#define NUM_ELEMENTS(_t)
Definition build.h:337
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition dcursor.h:288
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition dcursor.h:337
#define MEM(x)
Definition debug.h:36
Test enumeration values.
Definition dict_test.h:92
#define unlang_function_repeat_set(_request, _repeat)
Set a new repeat function for an existing function frame.
Definition function.h:89
#define unlang_function_push(_request, _func, _repeat, _signal, _sigmask, _top_frame, _uctx)
Push a generic function onto the unlang stack.
Definition function.h:111
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.
Definition groups.c:614
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.
Definition groups.c:346
fr_ldap_query_t * query
Current query performing group lookup.
Definition groups.c:66
static char const * null_attrs[]
Definition groups.c:37
char const * attrs[2]
For retrieving the group name.
Definition groups.c:75
ldap_group_xlat_ctx_t * xlat_ctx
Xlat context being evaluated.
Definition groups.c:74
int count
How many entries there are in values.
Definition groups.c:77
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.
Definition groups.c:443
void * uctx
Optional context for use in results parsing.
Definition groups.c:67
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.
Definition groups.c:279
static int userobj_dyn_free(ldap_group_userobj_dyn_ctx_t *group_ctx)
Ensure retrieved LDAP values are cleared up.
Definition groups.c:1105
char * group_name[LDAP_MAX_CACHEABLE+1]
List of group names which need resolving.
Definition groups.c:48
char * group_dn[LDAP_MAX_CACHEABLE+1]
List of group DNs which need resolving.
Definition groups.c:50
struct berval ** values
Values of the membership attribute to check.
Definition groups.c:76
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.
Definition groups.c:906
tmpl_t * filter_tmpl
Tmpl to expand into LDAP filter.
Definition groups.c:63
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.
Definition groups.c:251
char const * attrs[2]
For retrieving the group name.
Definition groups.c:65
rlm_ldap_t const * inst
Module instance.
Definition groups.c:43
fr_value_box_list_t expanded_filter
Values produced by expanding filter xlat.
Definition groups.c:64
fr_value_box_t * base_dn
The base DN to search for groups in.
Definition groups.c:61
fr_ldap_thread_trunk_t * ttrunk
Trunk on which to perform additional queries.
Definition groups.c:62
fr_pair_list_t groups
Temporary list to hold pairs.
Definition groups.c:46
fr_ldap_thread_trunk_t * ttrunk
Trunk on which to perform additional queries.
Definition groups.c:45
char ** dn
Current DN being resolved.
Definition groups.c:51
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.
Definition groups.c:574
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.
Definition groups.c:1117
char const * attrs[2]
For resolving name from DN.
Definition groups.c:52
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.
Definition groups.c:1155
TALLOC_CTX * list_ctx
In which to allocate pairs.
Definition groups.c:47
bool resolving_value
Is the current query resolving a DN from values.
Definition groups.c:82
unsigned int name_cnt
How many names need resolving.
Definition groups.c:49
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.
Definition groups.c:858
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.
Definition groups.c:700
static void ldap_group_userobj_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
Cancel a pending group lookup query.
Definition groups.c:88
static void ldap_group_groupobj_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
Cancel a pending group object lookup.
Definition groups.c:594
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.
Definition groups.c:111
fr_value_box_t * base_dn
The base DN to search for groups in.
Definition groups.c:44
static void ldap_dn2name_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
Cancel an in-progress DN to name lookup.
Definition groups.c:879
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.
Definition groups.c:162
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.
Definition groups.c:893
fr_ldap_query_t * query
Current query performing group resolution.
Definition groups.c:53
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.
Definition groups.c:383
char const * lookup_dn
The DN currently being looked up, when resolving DN to name.
Definition groups.c:79
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.
Definition groups.c:786
int value_no
The current entry in values being processed.
Definition groups.c:78
rlm_ldap_t const * inst
Module instance.
Definition groups.c:60
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.
Definition groups.c:741
char * group_name
Result of resolving the provided group DN as to a name.
Definition groups.c:80
fr_ldap_query_t * query
Current query doing a DN to name resolution.
Definition groups.c:81
Context to use when looking up group membership using group objects.
Definition groups.c:59
Context to use when resolving group membership from the user object.
Definition groups.c:42
Context to use when evaluating group membership from the user object in an xlat.
Definition groups.c:73
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition interpret.c:1403
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
Definition interpret.c:1764
#define UNLANG_SUB_FRAME
Definition interpret.h:36
size_t fr_ldap_util_normalise_dn(char *out, char const *in)
Normalise escape sequences in a DN.
Definition util.c:439
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.
Definition util.c:70
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.
Definition util.c:517
#define LDAP_MAX_FILTER_STR_LEN
Maximum length of an xlat expanded filter.
Definition base.h:110
bool fr_ldap_util_is_dn(char const *in, size_t inlen)
Check whether a string looks like a DN.
Definition util.c:211
LDAP * handle
libldap handle.
Definition base.h:333
fr_ldap_result_code_t ret
Result code.
Definition base.h:470
#define LDAP_MAX_CACHEABLE
Maximum number of groups we retrieve from the server for a given user which need resolving from name ...
Definition base.h:103
trunk_request_t * treq
Trunk request this query is associated with.
Definition base.h:456
char * fr_ldap_berval_to_string(TALLOC_CTX *ctx, struct berval const *in)
Convert a berval to a talloced string.
Definition util.c:390
int fr_ldap_box_escape(fr_value_box_t *vb, UNUSED void *uctx)
Definition util.c:110
fr_ldap_connection_t * ldap_conn
LDAP connection this query is running on.
Definition base.h:457
@ LDAP_RESULT_SUCCESS
Successfully got LDAP results.
Definition base.h:190
@ LDAP_RESULT_NO_RESULT
No results returned.
Definition base.h:194
@ LDAP_RESULT_BAD_DN
The requested DN does not exist.
Definition base.h:193
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.
Definition base.h:677
LDAPMessage * result
Head of LDAP results list.
Definition base.h:468
#define LDAP_MAX_GROUP_NAME_LEN
Maximum name of a group name.
Definition base.h:108
LDAP query structure.
Definition base.h:422
Thread LDAP trunk structure.
Definition base.h:399
LDAP * fr_ldap_handle_thread_local(void)
Get a thread local dummy LDAP handle.
Definition base.c:1106
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.
Definition base.c:709
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition log.h:443
#define RWDEBUG(fmt,...)
Definition log.h:361
#define RINDENT()
Indent R* messages by one level.
Definition log.h:430
talloc_free(reap)
@ FR_TYPE_STRING
String of printable characters.
#define check(_handle, _len_p)
Definition bio.c:44
int fr_pair_value_strdup(fr_pair_t *vp, char const *src, bool tainted)
Copy data into an "string" data type.
Definition pair.c:2634
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition pair.c:1345
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.
Definition pair.c:283
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition pair.c:46
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.
Definition pair.c:2784
#define fr_assert(_expr)
Definition rad_assert.h:38
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define RDEBUG_ENABLED2()
Definition radclient.h:50
#define RDEBUG2(fmt,...)
Definition radclient.h:54
#define RDEBUG_ENABLED()
Definition radclient.h:49
#define RETURN_MODULE_RCODE(_rcode)
Definition rcode.h:64
#define RETURN_MODULE_INVALID
Definition rcode.h:59
#define RETURN_MODULE_OK
Definition rcode.h:57
#define RETURN_MODULE_FAIL
Definition rcode.h:56
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:40
@ RLM_MODULE_INVALID
The module considers the request invalid.
Definition rcode.h:45
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:43
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:42
@ RLM_MODULE_NOTFOUND
User not found.
Definition rcode.h:47
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:48
#define RETURN_MODULE_NOTFOUND
Definition rcode.h:61
fr_dict_attr_t const * request_attr_request
Definition request.c:45
fr_dict_attr_t const * request_attr_control
Definition request.c:47
LDAP authorization and authentication module headers.
ldap_autz_call_env_t * call_env
Definition rlm_ldap.h:187
fr_ldap_thread_trunk_t * ttrunk
Definition rlm_ldap.h:186
rlm_ldap_t const * inst
Definition rlm_ldap.h:183
tmpl_t * group_filter
tmpl to expand as group membership filter.
Definition rlm_ldap.h:129
LDAPMessage * entry
Definition rlm_ldap.h:188
struct rlm_ldap_t::@161 group
fr_value_box_t group_base
Base DN in which to search for groups.
Definition rlm_ldap.h:128
char const * attrs[2]
Definition rlm_ldap.h:214
Holds state of in progress async authorization.
Definition rlm_ldap.h:181
Holds state of in progress group membership check xlat.
Definition rlm_ldap.h:208
static char const * name
#define pair_append_control(_attr, _da)
Allocate and append a fr_pair_t to the control list.
Definition pair.h:57
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.
Definition tmpl_eval.c:76
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.
Definition tmpl_eval.c:116
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:344
struct tmpl_rules_s tmpl_rules_t
Definition tmpl.h:236
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:341
fr_signal_t
Signals that can be generated/processed by request signal handlers.
Definition signal.h:38
@ FR_SIGNAL_CANCEL
Request has been cancelled.
Definition signal.h:40
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:41
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
return count
Definition module.c:163
eap_aka_sim_process_conf_t * inst
fr_pair_t * vp
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition tmpl.h:285
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition talloc.c:492
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:564
static int talloc_const_free(void const *ptr)
Free const'd memory.
Definition talloc.h:224
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.
Definition tmpl.c:262
@ TMPL_ESCAPE_PRE_CONCAT
Pre-concatenation escaping is useful for DSLs where elements of the expansion are static,...
Definition tmpl_escape.h:61
@ T_OP_CMP_EQ
Definition token.h:106
void trunk_request_signal_cancel(trunk_request_t *treq)
Cancel a trunk request.
Definition trunk.c:2152
#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.
Definition pair.h:628
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.
Definition pair_inline.c:70
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.
Definition pair_inline.c:43
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.
Definition value.c:929
#define fr_box_strvalue_buffer(_val)
Definition value.h:289
#define fr_box_strvalue_len(_val, _len)
Definition value.h:286
#define fr_box_strvalue(_val)
Definition value.h:285
uintptr_t fr_value_box_safe_for_t
Escaping that's been applied to a value box.
Definition value.h:155
static TALLOC_CTX * xlat_ctx