The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
groups.c
Go to the documentation of this file.
1/*
2 * This program 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: 9cf6caf5d7624d4f27e3888024d5b83ff036d110 $
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: 9cf6caf5d7624d4f27e3888024d5b83ff036d110 $")
28
30
31#include <freeradius-devel/util/debug.h>
32#include <freeradius-devel/server/rcode.h>
33
34#define LOG_PREFIX "rlm_ldap groups"
35
36#include "rlm_ldap.h"
37
38static char const *null_attrs[] = { NULL };
39
40/** Context to use when resolving group membership from the user object.
41 *
42 */
43typedef struct {
44 rlm_ldap_t const *inst; //!< Module instance.
45 fr_value_box_t *base_dn; //!< The base DN to search for groups in.
46 fr_ldap_thread_trunk_t *ttrunk; //!< Trunk on which to perform additional queries.
47 fr_pair_list_t groups; //!< Temporary list to hold pairs.
48 TALLOC_CTX *list_ctx; //!< In which to allocate pairs.
49 char *group_name[LDAP_MAX_CACHEABLE + 1]; //!< List of group names which need resolving.
50 unsigned int name_cnt; //!< How many names need resolving.
51 char *group_dn[LDAP_MAX_CACHEABLE + 1]; //!< List of group DNs which need resolving.
52 char **dn; //!< Current DN being resolved.
53 char const *attrs[2]; //!< For resolving name from DN.
54 fr_ldap_query_t *query; //!< Current query performing group resolution.
56
57/** Context to use when looking up group membership using group objects.
58 *
59 */
60typedef struct {
61 rlm_ldap_t const *inst; //!< Module instance.
62 fr_value_box_t *base_dn; //!< The base DN to search for groups in.
63 fr_ldap_thread_trunk_t *ttrunk; //!< Trunk on which to perform additional queries.
64 tmpl_t *filter_tmpl; //!< Tmpl to expand into LDAP filter.
65 fr_value_box_list_t expanded_filter; //!< Values produced by expanding filter xlat.
66 char const *attrs[2]; //!< For retrieving the group name.
67 fr_ldap_query_t *query; //!< Current query performing group lookup.
68 void *uctx; //!< Optional context for use in results parsing.
70
71/** Context to use when evaluating group membership from the user object in an xlat
72 *
73 */
74typedef struct {
75 ldap_group_xlat_ctx_t *xlat_ctx; //!< Xlat context being evaluated.
76 char const *attrs[2]; //!< For retrieving the group name.
77 struct berval **values; //!< Values of the membership attribute to check.
78 int count; //!< How many entries there are in values.
79 int value_no; //!< The current entry in values being processed.
80 char const *lookup_dn; //!< The DN currently being looked up, when resolving DN to name.
81 char *group_name; //!< Result of resolving the provided group DN as to a name.
82 fr_ldap_query_t *query; //!< Current query doing a DN to name resolution.
83 bool resolving_value; //!< Is the current query resolving a DN from values.
85
86/** Cancel a pending group lookup query
87 *
88 */
89static void ldap_group_userobj_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
90{
91 ldap_group_userobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_ctx_t);
92
93 /*
94 * If the query is not in flight, just return.
95 */
96 if (!group_ctx->query || !(group_ctx->query->treq)) return;
97
99}
100
101/** Convert multiple group names into a DNs
102 *
103 * Given an array of group names, builds a filter matching all names, then retrieves all group objects
104 * and stores the DN associated with each group object.
105 *
106 * @param[out] p_result The result of trying to resolve a group name to a dn.
107 * @param[in] request Current request.
108 * @param[in] uctx Group lookup context.
109 * @return One of the RLM_MODULE_* values.
110 */
112{
113 ldap_group_userobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_ctx_t);
114 rlm_ldap_t const *inst = group_ctx->inst;
115 char **name = group_ctx->group_name;
116 bool has_multiple = group_ctx->group_name[0] && group_ctx->group_name[1];
117 fr_sbuff_t sbuff;
118 fr_sbuff_uctx_talloc_t sbuff_ctx;
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 MEM(fr_sbuff_init_talloc(group_ctx, &sbuff, &sbuff_ctx, 256, SIZE_MAX));
136
137 if (inst->group.obj_filter) MEM(fr_sbuff_in_sprintf(&sbuff, "(&%s", inst->group.obj_filter) >= 0);
138 if (has_multiple) MEM(fr_sbuff_in_strcpy_literal(&sbuff, "(|") >= 0);
139 for (; *name; name++) {
140 MEM(fr_sbuff_in_sprintf(&sbuff, "(%s=", inst->group.obj_name_attr) >= 0);
142 MEM(fr_sbuff_in_char(&sbuff, ')') >= 0);
143
144 group_ctx->name_cnt++;
145 }
146 if (has_multiple) MEM(fr_sbuff_in_char(&sbuff, ')') >= 0);
147 if (inst->group.obj_filter) MEM(fr_sbuff_in_char(&sbuff, ')') >= 0);
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, fr_sbuff_buff(&sbuff),
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[in] request Current request.
158 * @param[in] uctx Group lookup context.
159 * @return One of the RLM_MODULE_* values.
160 */
162{
163 ldap_group_userobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_ctx_t);
164 fr_ldap_query_t *query = talloc_get_type_abort(group_ctx->query, fr_ldap_query_t);
165 rlm_ldap_t const *inst = group_ctx->inst;
167 unsigned int entry_cnt;
168 LDAPMessage *entry;
169 int ldap_errno;
170 char *dn;
171 fr_pair_t *vp;
172
173 switch (query->ret) {
175 break;
176
179 RDEBUG2("Tried to resolve group name(s) to DNs but got no results");
180 goto finish;
181
182 default:
183 rcode = RLM_MODULE_FAIL;
184 goto finish;
185 }
186
187 entry_cnt = ldap_count_entries(query->ldap_conn->handle, query->result);
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");
190 rcode = RLM_MODULE_INVALID;
191
192 goto finish;
193 }
194
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",
197 group_ctx->name_cnt, entry_cnt);
198 }
199
200 entry = ldap_first_entry(query->ldap_conn->handle, query->result);
201 if (!entry) {
202 ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
203 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
204
205 rcode = RLM_MODULE_FAIL;
206 goto finish;
207 }
208
209 do {
210 dn = ldap_get_dn(query->ldap_conn->handle, entry);
211 if (!dn) {
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));
214
215 rcode = RLM_MODULE_FAIL;
216 goto finish;
217 }
219
220 RDEBUG2("Got group DN \"%s\"", dn);
221 MEM(vp = fr_pair_afrom_da(group_ctx->list_ctx, inst->group.cache_da));
222 fr_pair_value_bstrndup(vp, dn, strlen(dn), true);
223 fr_pair_append(&group_ctx->groups, vp);
224 ldap_memfree(dn);
225 } while((entry = ldap_next_entry(query->ldap_conn->handle, entry)));
226
227finish:
228 /*
229 * Remove pointer to group name to resolve so we don't
230 * try to do it again
231 */
232 *group_ctx->group_name = NULL;
233 talloc_free(group_ctx->query);
234
235 RETURN_UNLANG_RCODE(rcode);
236}
237
238/** Initiate an LDAP search to turn a group DN into it's name
239 *
240 * Unlike the inverse conversion of a name to a DN, most LDAP directories don't allow filtering by DN,
241 * so we need to search for each DN individually.
242 *
243 * @param[out] p_result The result of trying to resolve a dn to a group name..
244 * @param[in] request Current request.
245 * @param[in] uctx The group resolution context.
246 * @return One of the RLM_MODULE_* values.
247 */
249{
250 ldap_group_userobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_ctx_t);
251 rlm_ldap_t const *inst = group_ctx->inst;
252
253 if (!inst->group.obj_name_attr) {
254 REDEBUG("Told to resolve group DN to name but missing 'group.name_attribute' directive");
256 }
257
258 RDEBUG2("Resolving group DN \"%s\" to group name", *group_ctx->dn);
259
260 return fr_ldap_trunk_search(group_ctx, &group_ctx->query, request, group_ctx->ttrunk, *group_ctx->dn,
261 LDAP_SCOPE_BASE, NULL, group_ctx->attrs, NULL, NULL);
262}
263
264/** Process the results of a group DN -> name lookup.
265 *
266 * The retrieved value is added as a value pair to the
267 * temporary list in the group resolution context.
268 *
269 * @param[out] p_result The result of trying to resolve a dn to a group name.
270 * @param[in] request Current request.
271 * @param[in] uctx The group resolution context.
272 * @return One of the RLM_MODULE_* values.
273 */
275{
276 ldap_group_userobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_ctx_t);
277 fr_ldap_query_t *query = talloc_get_type_abort(group_ctx->query, fr_ldap_query_t);
278 rlm_ldap_t const *inst = group_ctx->inst;
279 LDAPMessage *entry;
280 struct berval value;
281 int ldap_errno;
283 fr_pair_t *vp;
284
285 switch (query->ret) {
287 break;
288
291 REDEBUG("Group DN \"%s\" did not resolve to an object", *group_ctx->dn);
292 rcode = (inst->group.allow_dangling_refs ? RLM_MODULE_NOOP : RLM_MODULE_INVALID);
293 goto finish;
294
295 default:
296 rcode = RLM_MODULE_FAIL;
297 goto finish;
298 }
299
300 entry = ldap_first_entry(query->ldap_conn->handle, query->result);
301 if (!entry) {
302 ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
303 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
304 rcode = RLM_MODULE_INVALID;
305 goto finish;
306 }
307
308 if (fr_ldap_entry_value_find(&value, query->ldap_conn->handle, entry, inst->group.obj_name_attr) <= 0) {
309 REDEBUG("No %s attributes found in object", inst->group.obj_name_attr);
310 rcode = RLM_MODULE_INVALID;
311 goto finish;
312 }
313
314 MEM(vp = fr_pair_afrom_da(group_ctx->list_ctx, inst->group.cache_da));
315 fr_pair_value_bstrndup(vp, value.bv_val, value.bv_len, true);
316 fr_pair_append(&group_ctx->groups, vp);
317 RDEBUG2("Group DN \"%s\" resolves to name \"%pV\"", *group_ctx->dn, &vp->data);
318
319finish:
320 /*
321 * Walk the pointer to the DN being resolved forward
322 * ready for the next resolution.
323 */
324 group_ctx->dn++;
325
326 talloc_free(query);
327
328 RETURN_UNLANG_RCODE(rcode);
329}
330
331/** Move user object group attributes to the control list
332 *
333 * @param p_result The result of adding user object group attributes
334 * @param request Current request.
335 * @param group_ctx Context used to evaluate group attributes
336 * @return RLM_MODULE_OK
337 */
339 ldap_group_userobj_ctx_t *group_ctx)
340{
341 fr_pair_t *vp;
342 fr_pair_list_t *list;
343
344 list = tmpl_list_head(request, request_attr_control);
345 fr_assert(list != NULL);
346
347 RDEBUG2("Adding cacheable user object memberships");
348 RINDENT();
349 if (RDEBUG_ENABLED) {
350 for (vp = fr_pair_list_head(&group_ctx->groups);
351 vp;
352 vp = fr_pair_list_next(&group_ctx->groups, vp)) {
353 RDEBUG2("control.%s += \"%pV\"", group_ctx->inst->group.cache_da->name, &vp->data);
354 }
355 }
356
357 fr_pair_list_append(list, &group_ctx->groups);
358 REXDENT();
359
360 talloc_free(group_ctx);
362}
363
364/** Initiate DN to name and name to DN group lookups
365 *
366 * Called repeatedly until there are no more lookups to perform
367 * or an unresolved lookup causes the module to fail.
368 *
369 * @param p_result The result of the previous expansion.
370 * @param request Current request.
371 * @param uctx The group context being processed.
372 * @return One of the RLM_MODULE_* values.
373 */
375{
376 ldap_group_userobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_ctx_t);
377
378 /*
379 * If we've previously failed to expand, fail the group section
380 */
381 switch (p_result->rcode) {
382 case RLM_MODULE_FAIL:
384 talloc_free(group_ctx);
386 default:
387 break;
388 }
389
390 /*
391 * Are there any DN to resolve to names?
392 * These are resolved one at a time as most directories don't allow for
393 * filters on the DN.
394 */
395 if (*group_ctx->dn) {
397 if (unlang_function_push_with_result(/* both start and resume provide an rcode */p_result, request,
402 group_ctx) < 0) RETURN_UNLANG_FAIL;
404 }
405
406 /*
407 * Are there any names to resolve to DN?
408 */
409 if (*group_ctx->group_name) {
411 if (unlang_function_push_with_result(/* both start and resume provide an rcode */p_result, request,
416 group_ctx) < 0) RETURN_UNLANG_FAIL;
418 }
419
420 /*
421 * Nothing left to resolve, move the resulting attributes to
422 * the control list.
423 */
424 return ldap_cacheable_userobj_store(p_result, request, group_ctx);
425}
426
427/** Convert group membership information into attributes
428 *
429 * This may just be able to parse attribute values in the user object
430 * or it may need to yield to other LDAP searches depending on what was
431 * returned and what is set to be cached.
432 *
433 * @param[out] p_result The result of trying to resolve a dn to a group name.
434 * @param[in] request Current request.
435 * @param[in] autz_ctx LDAP authorization context being processed.
436 * @param[in] attr membership attribute to look for in the entry.
437 * @return One of the RLM_MODULE_* values.
438 */
440 char const *attr)
441{
442 rlm_ldap_t const *inst = autz_ctx->inst;
443 LDAPMessage *entry = autz_ctx->entry;
444 fr_ldap_thread_trunk_t *ttrunk = autz_ctx->ttrunk;
445 ldap_group_userobj_ctx_t *group_ctx;
447 struct berval *value;
448 char **name_p;
449 char **dn_p;
450 fr_pair_t *vp;
451 int is_dn, iter_err = 0, name2dn = 0, dn2name = 0;
452 size_t count = 0, strings_len = 0;
453 bool want_profiles = rlm_ldap_profile_attr_select(inst->group.profile_attr,
454 inst->group.profile_attr_suspend,
455 autz_ctx->access_state) != NULL;
456
457 fr_assert(entry);
459
460 /*
461 * Parse the membership information we got in the initial user query.
462 */
464 autz_ctx->query->result, attr) < 0)) {
465 RPERROR("Failed parsing user object");
467 }
468 if (count == 0) {
469 RDEBUG2("No cacheable group memberships found in user object");
470
472 }
473
474 /*
475 * Extended once for the whole batch, only DN valued
476 * memberships are appended.
477 */
478 if (want_profiles) {
479 if (!autz_ctx->group_dn_list) {
480 MEM(autz_ctx->group_dn_list = talloc_str_list_alloc(autz_ctx, count, strings_len));
481 } else {
483 }
484 }
485
486 /*
487 * Set up context for managing group membership attribute resolution.
488 */
489 MEM(group_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), ldap_group_userobj_ctx_t));
490 group_ctx->inst = inst;
491 group_ctx->ttrunk = ttrunk;
492 group_ctx->base_dn = &autz_ctx->call_env->group_base;
493 group_ctx->list_ctx = tmpl_list_ctx(request, request_attr_control);
494 fr_assert(group_ctx->list_ctx != NULL);
495
496 /*
497 * Set up pointers to entries in arrays of names / DNs to resolve.
498 */
499 name_p = group_ctx->group_name;
500 group_ctx->dn = dn_p = group_ctx->group_dn;
501
502 /*
503 * Temporary list to hold new group VPs, will be merged
504 * once all group info has been gathered/resolved
505 * successfully.
506 */
507 fr_pair_list_init(&group_ctx->groups);
508
509 for (value = fr_ldap_value_iter_init(&iter_err, &iter, fr_ldap_handle_thread_local(), entry, attr);
510 value;
511 value = fr_ldap_value_iter_next(&iter_err, &iter)) {
512 is_dn = fr_ldap_util_is_dn(value->bv_val, value->bv_len);
513
514 /*
515 * Record group object DNs for the later profile search.
516 * Name values are only resolved to DNs for caching, so
517 * profiles on groups referenced by name are not found.
518 */
519 if (want_profiles && is_dn) {
520 MEM(talloc_str_list_append(autz_ctx->group_dn_list, value->bv_val, value->bv_len));
521 }
522
523 if (inst->group.cacheable_dn) {
524 /*
525 * The easy case, we're caching DNs and we got a DN.
526 */
527 if (is_dn) {
528 MEM(vp = fr_pair_afrom_da(group_ctx->list_ctx, inst->group.cache_da));
529 fr_pair_value_bstrndup(vp, value->bv_val, value->bv_len, true);
530 fr_pair_append(&group_ctx->groups, vp);
531 /*
532 * We were told to cache DNs but we got a name, we now need to resolve
533 * this to a DN. Store all the group names in an array so we can do one query.
534 */
535 } else {
536 if (++name2dn > LDAP_MAX_CACHEABLE) {
537 REDEBUG("Too many groups require name to DN resolution");
538 invalid:
540 talloc_free(group_ctx);
542 }
543 *name_p++ = fr_ldap_berval_to_string(group_ctx, value);
544 }
545 }
546
547 if (inst->group.cacheable_name) {
548 /*
549 * The easy case, we're caching names and we got a name.
550 */
551 if (!is_dn) {
552 MEM(vp = fr_pair_afrom_da(group_ctx->list_ctx, inst->group.cache_da));
553 fr_pair_value_bstrndup(vp, value->bv_val, value->bv_len, true);
554 fr_pair_append(&group_ctx->groups, vp);
555 /*
556 * We were told to cache names but we got a DN, we now need to resolve
557 * this to a name. Store group DNs which need resolving to names.
558 */
559 } else {
560 if (++dn2name > LDAP_MAX_CACHEABLE) {
561 REDEBUG("Too many groups require DN to name resolution");
562 goto invalid;
563 }
564 *dn_p++ = fr_ldap_berval_to_string(group_ctx, value);
565 }
566 }
567 }
569 if (unlikely(iter_err < 0)) {
570 RPERROR("Failed parsing user object");
571 talloc_free(group_ctx);
573 }
574
575 /*
576 * We either have group names which need converting to DNs or
577 * DNs which need resolving to names. Push a function which will
578 * do the resolution.
579 */
580 if ((name_p != group_ctx->group_name) || (dn_p != group_ctx->group_dn)) {
581 group_ctx->attrs[0] = inst->group.obj_name_attr;
582 if (unlang_function_push_with_result(p_result, request,
584 NULL,
587 group_ctx) < 0) {
588 talloc_free(group_ctx);
590 }
592 }
593
594 /*
595 * No additional queries needed, just process the context to
596 * move any generated pairs into the correct list.
597 */
598 return ldap_cacheable_userobj_store(p_result, request, group_ctx);
599}
600
601/** Initiate an LDAP search for group membership looking at the group objects
602 *
603 * @param[out] p_result Result of submitting LDAP search
604 * @param[in] request Current request.
605 * @param[in] uctx Group lookup context.
606 * @return One of the RLM_MODULE_* values.
607 */
609{
610 ldap_group_groupobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_groupobj_ctx_t);
611 rlm_ldap_t const *inst = group_ctx->inst;
612 fr_value_box_t *filter;
613
614 filter = fr_value_box_list_head(&group_ctx->expanded_filter);
615
616 if (!filter || filter->type != FR_TYPE_STRING) RETURN_UNLANG_FAIL;
617
618 group_ctx->attrs[0] = inst->group.obj_name_attr;
619 return fr_ldap_trunk_search(group_ctx, &group_ctx->query, request, group_ctx->ttrunk,
620 group_ctx->base_dn->vb_strvalue, inst->group.obj_scope,
621 filter->vb_strvalue, group_ctx->attrs, NULL, NULL);
622}
623
624/** Cancel a pending group object lookup.
625 *
626 */
627static void ldap_group_groupobj_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
628{
629 ldap_group_groupobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_groupobj_ctx_t);
630
631 /*
632 * If the query is not in flight, just return
633 */
634 if (!group_ctx->query || !group_ctx->query->treq) return;
635
637}
638
639/** Process the results of a group object lookup.
640 *
641 * @param[out] p_result Result of processing group lookup.
642 * @param[in] request Current request.
643 * @param[in] uctx Group lookup context.
644 * @return One of the RLM_MODULE_* values.
645 */
647{
648 ldap_group_groupobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_groupobj_ctx_t);
649 ldap_autz_ctx_t *autz_ctx = talloc_get_type_abort(group_ctx->uctx, ldap_autz_ctx_t);
650 rlm_ldap_t const *inst = group_ctx->inst;
651 fr_ldap_query_t *query = group_ctx->query;
653 LDAPMessage *entry;
654 int ldap_errno;
655 char *dn;
656 fr_pair_t *vp;
657 bool want_profiles = rlm_ldap_profile_attr_select(inst->group.profile_attr,
658 inst->group.profile_attr_suspend,
659 autz_ctx->access_state) != NULL;
660
661 switch (query->ret) {
662 case LDAP_SUCCESS:
663 break;
664
667 RDEBUG2("No cacheable group memberships found in group objects");
668 rcode = RLM_MODULE_NOTFOUND;
669 goto finish;
670
671 default:
672 rcode = RLM_MODULE_FAIL;
673 goto finish;
674 }
675
676 entry = ldap_first_entry(query->ldap_conn->handle, query->result);
677 if (!entry) {
678 ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
679 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
680
681 goto finish;
682 }
683
684 if (want_profiles) {
685 size_t entry_cnt = (size_t)ldap_count_entries(query->ldap_conn->handle, query->result);
686
687 if (!autz_ctx->group_dn_list) {
688 MEM(autz_ctx->group_dn_list = talloc_str_list_alloc(autz_ctx, entry_cnt, 0));
689 } else {
690 MEM(talloc_str_list_realloc(autz_ctx->group_dn_list, entry_cnt) == 0);
691 }
692 }
693
694 RDEBUG2("Adding cacheable group object memberships");
695 do {
696 if (inst->group.cacheable_dn || want_profiles) {
697 dn = ldap_get_dn(query->ldap_conn->handle, entry);
698 if (!dn) {
699 ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
700 REDEBUG("Retrieving object DN from entry failed: %s", ldap_err2string(ldap_errno));
701
702 goto finish;
703 }
705
706 if (want_profiles) {
707 MEM(talloc_str_list_append(autz_ctx->group_dn_list, dn, strlen(dn)));
708 }
709
710 if (inst->group.cacheable_dn) {
711 MEM(pair_append_control(&vp, inst->group.cache_da) == 0);
712 fr_pair_value_strdup(vp, dn, false);
713
714 RINDENT();
715 RDEBUG2("control.%pP", vp);
716 REXDENT();
717 }
718 ldap_memfree(dn);
719 }
720
721 if (inst->group.cacheable_name) {
722 struct berval value;
723
724 if (fr_ldap_entry_value_find(&value, query->ldap_conn->handle, entry,
725 inst->group.obj_name_attr) <= 0) continue;
726
727 MEM(pair_append_control(&vp, inst->group.cache_da) == 0);
728 fr_pair_value_bstrndup(vp, value.bv_val, value.bv_len, true);
729
730 RINDENT();
731 RDEBUG2("control.%pP", vp);
732 REXDENT();
733 }
734 } while ((entry = ldap_next_entry(query->ldap_conn->handle, entry)));
735
736finish:
737 talloc_free(group_ctx);
738
739 RETURN_UNLANG_RCODE(rcode);
740}
741
742/** Convert group membership information into attributes
743 *
744 * @param[out] p_result The result of trying to resolve a dn to a group name.
745 * @param[in] request Current request.
746 * @param[in] autz_ctx Authentication context being processed.
747 * @return One of the RLM_MODULE_* values.
748 */
750{
751 rlm_ldap_t const *inst = autz_ctx->inst;
752 ldap_group_groupobj_ctx_t *group_ctx;
753
754 if (!inst->group.obj_membership_filter) {
755 RDEBUG2("Skipping caching group objects as directive 'group.membership_filter' is not set");
757 }
758
759 if (autz_ctx->call_env->group_base.type != FR_TYPE_STRING) {
760 REDEBUG("Missing group base_dn");
762 }
763
764 MEM(group_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), ldap_group_groupobj_ctx_t));
765 group_ctx->inst = inst;
766 group_ctx->ttrunk = autz_ctx->ttrunk;
767 group_ctx->base_dn = &autz_ctx->call_env->group_base;
768 group_ctx->uctx = autz_ctx;
769 fr_value_box_list_init(&group_ctx->expanded_filter);
770
772 request,
777 group_ctx) < 0) {
778 error:
779 talloc_free(group_ctx);
781 }
782
783 if (unlang_tmpl_push(group_ctx, NULL, &group_ctx->expanded_filter, request, autz_ctx->call_env->group_filter, NULL, UNLANG_SUB_FRAME) < 0) goto error;
784
786}
787
788/** Context used when searching for profiles in the user's group objects
789 *
790 */
791typedef struct {
792 ldap_autz_ctx_t *autz_ctx; //!< Authorization context profile DNs are harvested into.
793 char const *profile_attr; //!< Attribute holding profile DNs.
794 char const *filter; //!< Filter matching profile bearing group objects.
795 char const *attrs[2]; //!< For retrieving the profile attribute.
796 fr_ldap_query_t *query; //!< Current query retrieving the group objects.
798
799/** Harvest profile DNs from the group objects returned by the profile search
800 *
801 * @param[out] p_result Result of processing the search response.
802 * @param[in] request Current request.
803 * @param[in] uctx Group profile context.
804 * @return One of the RLM_MODULE_* values.
805 */
807{
808 ldap_group_profile_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_profile_ctx_t);
809 ldap_autz_ctx_t *autz_ctx = group_ctx->autz_ctx;
810 fr_ldap_query_t *query = group_ctx->query;
812
813 switch (query->ret) {
814 case LDAP_SUCCESS:
815 break;
816
819 RDEBUG2("No profiles found in group objects");
820 goto finish;
821
822 default:
823 rcode = RLM_MODULE_FAIL;
824 goto finish;
825 }
826
829 query->result, group_ctx->profile_attr, 0);
830 if (unlikely(!autz_ctx->group_profile_dn_list)) {
831 RPERROR("Failed parsing profiles from group objects");
832 rcode = RLM_MODULE_FAIL;
833 }
834
835finish:
836 talloc_free(group_ctx);
837
838 RETURN_UNLANG_RCODE(rcode);
839}
840
841/** Search for profile DNs in the group objects the user is a member of
842 *
843 * Retrieves only the groups which carry the profile attribute, using a
844 * single search matching the collected group DNs, with the profile
845 * attribute presence asserted in the filter.
846 *
847 * @param[out] p_result Result of submitting the search.
848 * @param[in] request Current request.
849 * @param[in] autz_ctx Authorization context, provides the group DNs
850 * and receives the profile DNs.
851 * @return One of the RLM_MODULE_* values.
852 */
854{
855 rlm_ldap_t const *inst = autz_ctx->inst;
856 ldap_group_profile_ctx_t *group_ctx;
857 char const *profile_attr = rlm_ldap_profile_attr_select(inst->group.profile_attr,
858 inst->group.profile_attr_suspend,
859 autz_ctx->access_state);
860 char const *dn_attr;
861 char const *base;
862
863 fr_assert(profile_attr);
864 fr_assert(autz_ctx->group_dn_list);
865
867 if (!base) {
868 RWDEBUG("Skipping group profiles, no naming context contains every group DN");
870 }
871
872 dn_attr = inst->dn_attr;
873 if (!dn_attr) dn_attr = autz_ctx->ttrunk->directory->dn_attr;
874
875 MEM(group_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), ldap_group_profile_ctx_t));
876 group_ctx->autz_ctx = autz_ctx;
877 group_ctx->profile_attr = profile_attr;
878 group_ctx->attrs[0] = profile_attr;
879 group_ctx->filter = fr_ldap_filter_afrom_dn_list(group_ctx, dn_attr,
880 talloc_asprintf(group_ctx, "(%s=*)", profile_attr),
881 autz_ctx->group_dn_list->strings);
882
883 /*
884 * No signal callback, the frame pushed by fr_ldap_trunk_search
885 * owns cancellation of the query.
886 */
887 if (unlang_function_push_with_result(p_result, request,
888 NULL,
890 NULL, 0, UNLANG_SUB_FRAME,
891 group_ctx) < 0) {
892 talloc_free(group_ctx);
894 }
895
896 return fr_ldap_trunk_search(group_ctx, &group_ctx->query, request, autz_ctx->ttrunk,
897 base, LDAP_SCOPE_SUB, group_ctx->filter, group_ctx->attrs, NULL, NULL);
898}
899
900/** Process the results of a group object lookup.
901 *
902 * @param[out] p_result Result of processing group lookup.
903 * @param[in] request Current request.
904 * @param[in] uctx Group lookup context.
905 * @return One of the RLM_MODULE_* values.
906 */
908{
909 ldap_group_groupobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_groupobj_ctx_t);
910 ldap_group_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(group_ctx->uctx, ldap_group_xlat_ctx_t);
911 fr_ldap_query_t *query = group_ctx->query;
913
914 switch (query->ret) {
915 case LDAP_SUCCESS:
916 xlat_ctx->found = true;
917 if (RDEBUG_ENABLED2) {
918 LDAPMessage *entry = NULL;
919 char *dn = NULL;
920 entry = ldap_first_entry(query->ldap_conn->handle, query->result);
921 if (entry) {
922 dn = ldap_get_dn(query->ldap_conn->handle, entry);
923 RDEBUG2("User found in group object \"%pV\"", fr_box_strvalue(dn));
924 ldap_memfree(dn);
925 }
926 }
927 break;
928
931 rcode = RLM_MODULE_NOTFOUND;
932 break;
933
934 default:
935 rcode = RLM_MODULE_FAIL;
936 break;
937 }
938
939 talloc_free(group_ctx);
940 RETURN_UNLANG_RCODE(rcode);
941}
942
943/** Initiate an LDAP search to determine group membership, querying group objects
944 *
945 * Used by LDAP group membership xlat
946 *
947 * @param p_result Current module result code.
948 * @param request Current request.
949 * @param xlat_ctx xlat context being processed.
950 */
953{
954 rlm_ldap_t const *inst = xlat_ctx->inst;
955 ldap_group_groupobj_ctx_t *group_ctx;
956
958 *group_ctx = (ldap_group_groupobj_ctx_t) {
959 .inst = inst,
960 .ttrunk = xlat_ctx->ttrunk,
961 .uctx = xlat_ctx
962 };
963 fr_value_box_list_init(&group_ctx->expanded_filter);
964
965 if (fr_ldap_util_is_dn(xlat_ctx->group->vb_strvalue, xlat_ctx->group->vb_length)) {
966 group_ctx->filter_tmpl = xlat_ctx->env_data->group_filter;
967 group_ctx->base_dn = xlat_ctx->group;
968 } else {
969 char name_filter[LDAP_MAX_FILTER_STR_LEN];
970 char const *filters[] = { name_filter, inst->group.obj_filter, inst->group.obj_membership_filter };
971 tmpl_rules_t t_rules;
972
973 if (!inst->group.obj_name_attr) {
974 REDEBUG("Told to search for group by name, but missing 'group.name_attribute' "
975 "directive");
976 invalid:
977 talloc_free(group_ctx);
979 }
980
981 t_rules = (tmpl_rules_t){
982 .attr = {
983 .dict_def = request->local_dict,
984 .list_def = request_attr_request,
985 },
986 .xlat = {
987 .runtime_el = unlang_interpret_event_list(request),
988 },
989 .at_runtime = true,
990 .escape.box_escape = (fr_value_box_escape_t) {
992 .safe_for = LDAP_FILTER_SAFE_FOR,
993 .always_escape = false,
994 },
995 .escape.mode = TMPL_ESCAPE_PRE_CONCAT,
996 .literals_safe_for = LDAP_FILTER_SAFE_FOR,
997 .cast = FR_TYPE_STRING,
998 };
999
1000 snprintf(name_filter, sizeof(name_filter), "(%s=%s)",
1001 inst->group.obj_name_attr, xlat_ctx->group->vb_strvalue);
1002
1003 if (fr_ldap_filter_to_tmpl(group_ctx, &t_rules, filters, NUM_ELEMENTS(filters),
1004 &group_ctx->filter_tmpl) < 0) goto invalid;
1005
1006 fr_assert(xlat_ctx->env_data);
1007 group_ctx->base_dn = &xlat_ctx->env_data->group_base;
1008 }
1009
1011 request,
1016 group_ctx) < 0) {
1017 error:
1018 talloc_free(group_ctx);
1020 }
1021
1022 if (unlang_tmpl_push(group_ctx, NULL, &group_ctx->expanded_filter, request, group_ctx->filter_tmpl, NULL, UNLANG_SUB_FRAME) < 0) goto error;
1023
1025}
1026
1027/** Initiate resolving a group DN to its name
1028 *
1029 */
1030static unlang_action_t ldap_dn2name_start(unlang_result_t *p_result, request_t *request, void *uctx)
1031{
1032 ldap_group_userobj_dyn_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_dyn_ctx_t);
1034 rlm_ldap_t const *inst = xlat_ctx->inst;
1035
1036 if (!inst->group.obj_name_attr) {
1037 REDEBUG("Told to resolve group DN to name but missing 'group.name_attribute' directive");
1039 }
1040
1041 RDEBUG2("Resolving group DN \"%pV\" to group name", fr_box_strvalue_buffer(group_ctx->lookup_dn));
1042
1043 return fr_ldap_trunk_search(group_ctx, &group_ctx->query, request, xlat_ctx->ttrunk,
1044 group_ctx->lookup_dn, LDAP_SCOPE_BASE, NULL, group_ctx->attrs,
1045 NULL, NULL);
1046}
1047
1048/** Cancel an in-progress DN to name lookup.
1049 *
1050 */
1051static void ldap_dn2name_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
1052{
1053 ldap_group_userobj_dyn_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_dyn_ctx_t);
1054
1055 if (!group_ctx->query || !group_ctx->query->treq) return;
1056
1058}
1059
1060/** Initiate a user lookup to check membership.
1061 *
1062 * Used when the user's DN is already known but cached group membership has not been stored
1063 *
1064 */
1066 request_t *request, void *uctx)
1067{
1068 ldap_group_userobj_dyn_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_dyn_ctx_t);
1069 ldap_group_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(group_ctx->xlat_ctx, ldap_group_xlat_ctx_t);
1070
1071 return fr_ldap_trunk_search(xlat_ctx, &xlat_ctx->query, request, xlat_ctx->ttrunk, xlat_ctx->dn,
1072 LDAP_SCOPE_BASE, NULL, xlat_ctx->attrs, NULL, NULL);
1073}
1074
1075/** Process the results of evaluating a user object when checking group membership
1076 *
1077 * Any information relating to the user's group memberships should be evailable in the group_ctx
1078 * structure before this is called.
1079 */
1081{
1082 ldap_group_userobj_dyn_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_dyn_ctx_t);
1083 ldap_group_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(group_ctx->xlat_ctx, ldap_group_xlat_ctx_t);
1084 rlm_ldap_t const *inst = xlat_ctx->inst;
1085 fr_ldap_query_t *query = xlat_ctx->query;
1086 LDAPMessage *entry;
1087 int ldap_errno;
1088 bool value_is_dn = false;
1089 fr_value_box_t *group = xlat_ctx->group;
1090 char *value_name = NULL;
1091
1092 /*
1093 * If group_ctx->values is not populated, this is the first call
1094 * - extract the returned values if any.
1095 */
1096 if (!group_ctx->values) {
1097 entry = ldap_first_entry(query->ldap_conn->handle, query->result);
1098 if (!entry) {
1099 ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
1100 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
1102 }
1103
1104 group_ctx->values = ldap_get_values_len(query->ldap_conn->handle, entry, inst->group.userobj_membership_attr);
1105 if (!group_ctx->values) {
1106 RDEBUG2("User object contains no group membership information in attribute \"%s\"",
1107 inst->group.userobj_membership_attr);
1109 }
1110
1111 /*
1112 * To avoid re-assessing after each call out to do a DN -> name
1113 * lookup, cache this.
1114 */
1115 group_ctx->count = ldap_count_values_len(group_ctx->values);
1116 }
1117
1118 /*
1119 * Following a call out to do a DN -> name lookup, group_ctx->query will be
1120 * populated - process the results.
1121 */
1122 if (group_ctx->query) {
1123 char *buff;
1124 struct berval name_value;
1125
1126 switch (group_ctx->query->ret) {
1128 break;
1129
1131 case LDAP_RESULT_BAD_DN:
1132 REDEBUG("Group DN \"%pV\" did not resolve to an object",
1133 fr_box_strvalue_buffer(group_ctx->lookup_dn));
1135
1136 default:
1138 }
1139
1140 entry = ldap_first_entry(group_ctx->query->ldap_conn->handle, group_ctx->query->result);
1141 if (!entry) {
1142 ldap_get_option(group_ctx->query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
1143 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
1145 }
1146
1147 if (fr_ldap_entry_value_find(&name_value, group_ctx->query->ldap_conn->handle, entry,
1148 inst->group.obj_name_attr) <= 0) {
1149 REDEBUG("No %s attributes found in object", inst->group.obj_name_attr);
1151 }
1152
1153 MEM(buff = talloc_bstrndup(group_ctx, name_value.bv_val, name_value.bv_len));
1154 RDEBUG2("Group DN \"%pV\" resolves to name \"%pV\"", fr_box_strvalue_buffer(group_ctx->lookup_dn),
1155 fr_box_strvalue_len(name_value.bv_val, name_value.bv_len));
1156
1157 if (group_ctx->resolving_value) {
1158 value_name = buff;
1159 } else {
1160 group_ctx->group_name = buff;
1161 }
1162 }
1163
1164 /*
1165 * Loop over the list of groups the user is a member of, looking for a match.
1166 */
1167 while (group_ctx->value_no < group_ctx->count) {
1168 struct berval *value = group_ctx->values[group_ctx->value_no];
1169
1170 /*
1171 * We have come back from resolving a membership DN to its name,
1172 * compare to the provided name.
1173 */
1174 if (value_name && group_ctx->resolving_value) {
1175 if (((talloc_strlen(value_name)) == group->vb_length) &&
1176 (memcmp(group->vb_strvalue, value_name, group->vb_length) == 0)) {
1177 RDEBUG2("User found in group \"%pV\". Comparison between membership: name "
1178 "(resolved from DN \"%pV\"), check: name", group,
1179 fr_box_strvalue_buffer(group_ctx->lookup_dn));
1180 talloc_free(value_name);
1181 goto found;
1182 }
1183 talloc_const_free(group_ctx->lookup_dn);
1184 TALLOC_FREE(value_name);
1185 group_ctx->resolving_value = false;
1186 group_ctx->value_no++;
1187 continue;
1188 }
1189
1190 value_is_dn = fr_ldap_util_is_dn(value->bv_val, value->bv_len);
1191
1192 RDEBUG2("Processing %s value \"%pV\" as a %s", inst->group.userobj_membership_attr,
1193 fr_box_strvalue_len(value->bv_val, value->bv_len),
1194 value_is_dn ? "DN" : "group name");
1195
1196 /*
1197 * Both literal group names, do case sensitive comparison
1198 */
1199 if (!xlat_ctx->group_is_dn && !value_is_dn) {
1200 if ((group->vb_length == value->bv_len) &&
1201 (memcmp(value->bv_val, group->vb_strvalue, value->bv_len) == 0)) {
1202 RDEBUG2("User found in group \"%pV\". Comparison between membership: name, check: name",
1203 group);
1204 goto found;
1205 }
1206 group_ctx->value_no++;
1207 continue;
1208 }
1209
1210 /*
1211 * Both DNs, do case insensitive, binary safe comparison
1212 */
1213 if (xlat_ctx->group_is_dn && value_is_dn) {
1214 if (fr_ldap_berval_strncasecmp(value, group->vb_strvalue, group->vb_length) == 0) {
1215 RDEBUG2("User found in group DN \"%pV\". "
1216 "Comparison between membership: dn, check: dn", group);
1217 goto found;
1218 }
1219 group_ctx->value_no++;
1220 continue;
1221 }
1222
1223 /*
1224 * If the value is not a DN, and the name we were given is a dn
1225 * convert the value to a DN and do a comparison.
1226 */
1227 if (!value_is_dn && xlat_ctx->group_is_dn) {
1228 /*
1229 * So we only do the DN -> name lookup once, regardless of how many
1230 * group values we have to check, the resolved name is put in group_ctx->group_name
1231 */
1232 if (!group_ctx->group_name) {
1233 group_ctx->lookup_dn = group->vb_strvalue;
1234
1236
1237 /* Need to push this for the custom cancellation function */
1238 return unlang_function_push_with_result(p_result,
1239 request,
1241 NULL,
1244 group_ctx);
1245 }
1246
1247 if (((talloc_strlen(group_ctx->group_name)) == value->bv_len) &&
1248 (memcmp(value->bv_val, group_ctx->group_name, value->bv_len) == 0)) {
1249 RDEBUG2("User found in group \"%pV\". Comparison between membership: "
1250 "name, check: name (resolved from DN \"%pV\")",
1251 fr_box_strvalue_len(value->bv_val, value->bv_len), group);
1252 goto found;
1253 }
1254 group_ctx->value_no++;
1255 continue;
1256 }
1257
1258 /*
1259 * We have a value which is a DN, and a check item which specifies the name of a group,
1260 * convert the value to a name so we can do a comparison.
1261 */
1262 if (value_is_dn && !xlat_ctx->group_is_dn) {
1263 group_ctx->lookup_dn = fr_ldap_berval_to_string(group_ctx, value);
1264 group_ctx->resolving_value = true;
1265
1267
1268 /* Need to push this for the custom cancellation function */
1269 return unlang_function_push_with_result(p_result,
1270 request,
1272 NULL,
1274 UNLANG_SUB_FRAME, group_ctx);
1275 }
1276
1277 fr_assert(0);
1278 }
1280
1281found:
1282 xlat_ctx->found = true;
1284}
1285
1286/** Ensure retrieved LDAP values are cleared up
1287 *
1288 */
1290{
1291 if (group_ctx->values) ldap_value_free_len(group_ctx->values);
1292 return 0;
1293}
1294
1295/** Query the LDAP directory to check if a user object is a member of a group
1296 *
1297 * @param[out] p_result Result of calling the module.
1298 * @param[in] request Current request.
1299 * @param[in] xlat_ctx Context of the xlat being evaluated.
1300 */
1303{
1304 rlm_ldap_t const *inst = xlat_ctx->inst;
1306
1308 talloc_set_destructor(group_ctx, userobj_dyn_free);
1309
1310 *group_ctx = (ldap_group_userobj_dyn_ctx_t) {
1311 .xlat_ctx = xlat_ctx,
1312 .attrs = { inst->group.obj_name_attr, NULL }
1313 };
1314
1315 RDEBUG2("Checking user object's %s attributes", inst->group.userobj_membership_attr);
1316
1317 /*
1318 * If a previous query was required to find the user DN, that will have
1319 * retrieved the user object membership attribute and the resulting values
1320 * can be checked.
1321 * If not then a query is needed to retrieve the user object.
1322 */
1324 request,
1325 xlat_ctx->query ? NULL : ldap_check_userobj_start,
1327 ldap_group_userobj_cancel, ~FR_SIGNAL_CANCEL,
1329 group_ctx) < 0) {
1330 talloc_free(group_ctx);
1332 }
1333
1335}
1336
1337/** Check group membership attributes to see if a user is a member.
1338 *
1339 * @param[out] p_result Result of calling the module.
1340 * @param[in] inst rlm_ldap configuration.
1341 * @param[in] request Current request.
1342 * @param[in] check vb containing the group value (name or dn).
1343 */
1345 rlm_ldap_t const *inst, request_t *request, fr_value_box_t const *check)
1346{
1347 fr_pair_t *vp;
1348 int ret;
1349 fr_dcursor_t cursor;
1350
1351 /*
1352 * We return RLM_MODULE_INVALID here as an indication
1353 * the caller should try a dynamic group lookup instead.
1354 */
1355 vp = fr_pair_dcursor_by_da_init(&cursor, &request->control_pairs, inst->group.cache_da);
1357
1358 for (vp = fr_dcursor_current(&cursor);
1359 vp;
1360 vp = fr_dcursor_next(&cursor)) {
1361 ret = fr_value_box_cmp_op(T_OP_CMP_EQ, &vp->data, check);
1362 if (ret == 1) {
1363 RDEBUG2("User found. Matched cached membership");
1365 }
1366
1367 if (ret < -1) RETURN_UNLANG_FAIL;
1368 }
1369
1370 RDEBUG2("Cached membership not found");
1371
1373}
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
#define USES_APPLE_DEPRECATED_API
Definition build.h:547
#define RCSID(id)
Definition build.h:560
#define unlikely(_x)
Definition build.h:455
#define UNUSED
Definition build.h:384
#define NUM_ELEMENTS(_t)
Definition build.h:406
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:38
Test enumeration values.
Definition dict_test.h:92
Definition dwarf.c:424
#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.
Definition function.h:144
#define unlang_function_repeat_set(_request, _repeat)
Set a new repeat function for an existing function frame.
Definition function.h:108
fr_ldap_query_t * query
Current query performing group lookup.
Definition groups.c:67
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.
Definition groups.c:1065
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.
Definition groups.c:749
static char const * null_attrs[]
Definition groups.c:38
char const * attrs[2]
For retrieving the group name.
Definition groups.c:76
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.
Definition groups.c:608
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.
Definition groups.c:111
char const * attrs[2]
For retrieving the profile attribute.
Definition groups.c:795
ldap_group_xlat_ctx_t * xlat_ctx
Xlat context being evaluated.
Definition groups.c:75
int count
How many entries there are in values.
Definition groups.c:78
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.
Definition groups.c:951
void * uctx
Optional context for use in results parsing.
Definition groups.c:68
static int userobj_dyn_free(ldap_group_userobj_dyn_ctx_t *group_ctx)
Ensure retrieved LDAP values are cleared up.
Definition groups.c:1289
char * group_name[LDAP_MAX_CACHEABLE+1]
List of group names which need resolving.
Definition groups.c:49
char * group_dn[LDAP_MAX_CACHEABLE+1]
List of group DNs which need resolving.
Definition groups.c:51
struct berval ** values
Values of the membership attribute to check.
Definition groups.c:77
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.
Definition groups.c:1080
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.
Definition groups.c:439
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.
Definition groups.c:374
tmpl_t * filter_tmpl
Tmpl to expand into LDAP filter.
Definition groups.c:64
char const * attrs[2]
For retrieving the group name.
Definition groups.c:66
rlm_ldap_t const * inst
Module instance.
Definition groups.c:44
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.
Definition groups.c:1301
fr_value_box_list_t expanded_filter
Values produced by expanding filter xlat.
Definition groups.c:65
fr_value_box_t * base_dn
The base DN to search for groups in.
Definition groups.c:62
fr_ldap_thread_trunk_t * ttrunk
Trunk on which to perform additional queries.
Definition groups.c:63
fr_pair_list_t groups
Temporary list to hold pairs.
Definition groups.c:47
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.
Definition groups.c:646
fr_ldap_thread_trunk_t * ttrunk
Trunk on which to perform additional queries.
Definition groups.c:46
char ** dn
Current DN being resolved.
Definition groups.c:52
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.
Definition groups.c:338
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.
Definition groups.c:161
char const * attrs[2]
For resolving name from DN.
Definition groups.c:53
char const * filter
Filter matching profile bearing group objects.
Definition groups.c:794
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.
Definition groups.c:274
TALLOC_CTX * list_ctx
In which to allocate pairs.
Definition groups.c:48
bool resolving_value
Is the current query resolving a DN from values.
Definition groups.c:83
unsigned int name_cnt
How many names need resolving.
Definition groups.c:50
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.
Definition groups.c:1030
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:89
static unlang_action_t ldap_group_profile_resume(unlang_result_t *p_result, request_t *request, void *uctx)
Harvest profile DNs from the group objects returned by the profile search.
Definition groups.c:806
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:627
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.
Definition groups.c:907
fr_value_box_t * base_dn
The base DN to search for groups in.
Definition groups.c:45
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:1051
unlang_action_t rlm_ldap_group_profiles(unlang_result_t *p_result, request_t *request, ldap_autz_ctx_t *autz_ctx)
Search for profile DNs in the group objects the user is a member of.
Definition groups.c:853
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.
Definition groups.c:1344
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.
Definition groups.c:248
fr_ldap_query_t * query
Current query performing group resolution.
Definition groups.c:54
ldap_autz_ctx_t * autz_ctx
Authorization context profile DNs are harvested into.
Definition groups.c:792
char const * profile_attr
Attribute holding profile DNs.
Definition groups.c:793
char const * lookup_dn
The DN currently being looked up, when resolving DN to name.
Definition groups.c:80
fr_ldap_query_t * query
Current query retrieving the group objects.
Definition groups.c:796
int value_no
The current entry in values being processed.
Definition groups.c:79
rlm_ldap_t const * inst
Module instance.
Definition groups.c:61
char * group_name
Result of resolving the provided group DN as to a name.
Definition groups.c:81
fr_ldap_query_t * query
Current query doing a DN to name resolution.
Definition groups.c:82
Context to use when looking up group membership using group objects.
Definition groups.c:60
Context used when searching for profiles in the user's group objects.
Definition groups.c:791
Context to use when resolving group membership from the user object.
Definition groups.c:43
Context to use when evaluating group membership from the user object in an xlat.
Definition groups.c:74
talloc_free(hp)
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition interpret.c:2053
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
Definition interpret.c:2538
#define UNLANG_SUB_FRAME
Definition interpret.h:37
rlm_rcode_t rcode
The current rcode, from executing the instruction or merging the result from a frame.
Definition interpret.h:140
struct berval * fr_ldap_value_iter_init(int *err, fr_ldap_value_iter_t *iter, LDAP *handle, LDAPMessage *entry, char const *attr)
Start an in place iteration over an attribute's values in an entry.
Definition util.c:523
size_t fr_ldap_util_normalise_dn(char *out, char const *in)
Normalise escape sequences in a DN.
Definition util.c:794
char const * fr_ldap_directory_common_base_find(fr_ldap_directory_t const *directory, char const *const *dn_list)
Find the naming context which contains a set of DNs.
Definition directory.c:296
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:910
#define LDAP_MAX_FILTER_STR_LEN
Maximum length of an xlat expanded filter.
Definition base.h:109
int fr_ldap_entry_value_find(struct berval *out, LDAP *handle, LDAPMessage *entry, char const *attr)
Find an attribute in an entry, returning its first value referenced in place.
Definition util.c:715
int fr_ldap_filter_box_escape(fr_value_box_t *vb, UNUSED void *uctx)
Escape a value box for use as an RFC 4515 filter assertion value.
Definition util.c:172
void fr_ldap_value_iter_done(fr_ldap_value_iter_t *iter)
Release value iteration state.
Definition util.c:474
bool fr_ldap_util_is_dn(char const *in, size_t inlen)
Check whether a string looks like a DN.
Definition util.c:242
struct berval * fr_ldap_value_iter_next(int *err, fr_ldap_value_iter_t *iter)
Return the next value of the iterated attribute.
Definition util.c:490
LDAP * handle
libldap handle.
Definition base.h:342
fr_ldap_result_code_t ret
Result code.
Definition base.h:472
#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:458
#define LDAP_FILTER_SAFE_FOR
Marks a value box as already escaped for use as a filter assertion value.
Definition base.h:1016
int fr_ldap_result_values_len(size_t *num, size_t *strings_len, LDAP *handle, LDAPMessage *result, char const *attr)
Sum the lengths of an attribute's values across every entry of a result.
Definition util.c:620
fr_slen_t fr_ldap_filter_escape(fr_sbuff_t *out, fr_sbuff_t *in)
Escape a value for use as an RFC 4515 filter assertion value.
Definition util.c:144
char * fr_ldap_berval_to_string(TALLOC_CTX *ctx, struct berval const *in)
Convert a berval to a talloced string.
Definition util.c:745
talloc_str_list_t * fr_ldap_str_list_afrom_result(TALLOC_CTX *ctx, LDAP *handle, LDAPMessage *result, char const *attr, size_t extra)
Copy an attribute's values from every entry of a result into a string list.
Definition util.c:663
fr_ldap_connection_t * ldap_conn
LDAP connection this query is running on.
Definition base.h:459
@ 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:679
LDAPMessage * result
Head of LDAP results list.
Definition base.h:470
fr_ldap_directory_t * directory
The type of directory we're connected to.
Definition base.h:406
char const * dn_attr
Attribute to match an entry's DN in a search filter.
Definition base.h:213
char * fr_ldap_filter_afrom_dn_list(TALLOC_CTX *ctx, char const *dn_attr, char const *filter, char const *const *dn_list)
Build a filter matching a set of objects by DN.
Definition util.c:876
LDAP query structure.
Definition base.h:424
Thread LDAP trunk structure.
Definition base.h:401
State of an in place iteration over an attribute's values.
Definition base.h:965
LDAP * fr_ldap_handle_thread_local(void)
Get a thread local dummy LDAP handle.
Definition base.c:1131
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:719
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition log.h:460
#define RWDEBUG(fmt,...)
Definition log.h:378
#define RPERROR(fmt,...)
Definition log.h:319
#define RINDENT()
Indent R* messages by one level.
Definition log.h:447
@ FR_TYPE_STRING
String of printable characters.
unsigned long int size_t
int fr_pair_value_strdup(fr_pair_t *vp, char const *src, bool tainted)
Copy data into an "string" data type.
Definition pair.c:2595
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:1298
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:291
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition pair.c:47
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:2744
#define fr_assert(_expr)
Definition rad_assert.h:37
#define REDEBUG(fmt,...)
#define RDEBUG_ENABLED2()
#define RDEBUG2(fmt,...)
#define RDEBUG_ENABLED()
#define RETURN_UNLANG_INVALID
Definition rcode.h:66
#define RETURN_UNLANG_RCODE(_rcode)
Definition rcode.h:61
#define RETURN_UNLANG_NOTFOUND
Definition rcode.h:68
#define RETURN_UNLANG_FAIL
Definition rcode.h:63
#define RETURN_UNLANG_REJECT
Definition rcode.h:62
#define RETURN_UNLANG_OK
Definition rcode.h:64
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:44
@ RLM_MODULE_INVALID
The module considers the request invalid.
Definition rcode.h:51
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:49
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:48
@ RLM_MODULE_NOTFOUND
User not found.
Definition rcode.h:53
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:54
fr_dict_attr_t const * request_attr_request
Definition request.c:43
fr_dict_attr_t const * request_attr_control
Definition request.c:45
LDAP authorization and authentication module headers.
static char const * rlm_ldap_profile_attr_select(char const *attr, char const *attr_suspend, ldap_access_state_t access_state)
Return the profile attribute matching the user's access state.
Definition rlm_ldap.h:249
ldap_autz_call_env_t * call_env
Definition rlm_ldap.h:230
fr_ldap_thread_trunk_t * ttrunk
Definition rlm_ldap.h:229
rlm_ldap_t const * inst
Definition rlm_ldap.h:226
tmpl_t * group_filter
tmpl to expand as group membership filter.
Definition rlm_ldap.h:171
LDAPMessage * entry
Definition rlm_ldap.h:231
fr_ldap_query_t * query
Definition rlm_ldap.h:228
fr_value_box_t group_base
Base DN in which to search for groups.
Definition rlm_ldap.h:170
ldap_access_state_t access_state
What state a user's account is in.
Definition rlm_ldap.h:240
talloc_str_list_t * group_profile_dn_list
Profile DNs found in the user's group objects.
Definition rlm_ldap.h:237
struct rlm_ldap_t::@196 group
char const * attrs[2]
Definition rlm_ldap.h:283
talloc_str_list_t * group_dn_list
DNs of the group objects the user is a member of.
Definition rlm_ldap.h:236
Holds state of in progress async authorization.
Definition rlm_ldap.h:224
Holds state of in progress group membership check xlat.
Definition rlm_ldap.h:277
static char const * name
ssize_t fr_sbuff_in_sprintf(fr_sbuff_t *sbuff, char const *fmt,...)
Print using a fmt string to an sbuff.
Definition sbuff.c:1627
#define fr_sbuff_buff(_sbuff_or_marker)
#define FR_SBUFF_IN_STR(_start)
#define fr_sbuff_in_strcpy_literal(_sbuff, _str)
#define fr_sbuff_in_char(_sbuff,...)
Talloc sbuff extension structure.
Definition sbuff.h:137
#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:70
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:110
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:339
struct tmpl_rules_s tmpl_rules_t
Definition tmpl.h:233
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:336
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:37
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
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:273
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
int talloc_str_list_realloc(talloc_str_list_t *list, size_t extra)
Extend a string list to hold additional strings.
Definition talloc.c:923
talloc_str_list_t * talloc_str_list_alloc(TALLOC_CTX *ctx, size_t num, size_t strings_len)
Allocate a list to hold num strings of strings_len total length.
Definition talloc.c:888
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:618
char const * talloc_str_list_append(talloc_str_list_t *list, char const *str, size_t len)
Append a copy of a string to a string list.
Definition talloc.c:954
static int talloc_const_free(void const *ptr)
Free const'd memory.
Definition talloc.h:288
char const ** strings
NULL terminated array of strings.
Definition talloc.h:254
#define talloc_asprintf
Definition talloc.h:151
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:143
void check(const char *name, int index, const struct info *all, int want_lineno, const char *want_function, const char *want_file, int *failed)
Definition testlib.c:72
const char * base(const char *p)
Definition testlib.c:55
int unlang_tmpl_push(TALLOC_CTX *ctx, unlang_result_t *p_result, fr_value_box_list_t *out, request_t *request, tmpl_t const *tmpl, unlang_tmpl_args_t *args, bool top_frame)
Push a tmpl onto the stack for evaluation.
Definition tmpl.c:276
@ 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:104
void trunk_request_signal_cancel(trunk_request_t *treq)
Cancel a trunk request.
Definition trunk.c:2216
static unsigned count
Definition unittest.c:47
#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:636
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:69
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:42
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:1008
#define fr_box_strvalue_buffer(_val)
Definition value.h:337
#define fr_box_strvalue_len(_val, _len)
Definition value.h:334
#define fr_box_strvalue(_val)
Definition value.h:333
static TALLOC_CTX * xlat_ctx