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: aa79d131dbd05dcfab0dcda7ec322bddcf6b534a $
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: aa79d131dbd05dcfab0dcda7ec322bddcf6b534a $")
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;
117 char *filter;
118
119 if (!inst->group.obj_name_attr) {
120 REDEBUG("Told to convert group names to DNs but missing 'group.name_attribute' directive");
122 }
123 if (group_ctx->base_dn->type != FR_TYPE_STRING) {
124 REDEBUG("Missing group base_dn");
126 }
127
128 RDEBUG2("Converting group name(s) to group DN(s)");
129
130 /*
131 * It'll probably only save a few ms in network latency, but it means we can send a query
132 * for the entire group list at once.
133 */
134 filter = talloc_typed_asprintf(group_ctx, "%s%s%s",
135 inst->group.obj_filter ? "(&" : "",
136 inst->group.obj_filter ? inst->group.obj_filter : "",
137 group_ctx->group_name[0] && group_ctx->group_name[1] ? "(|" : "");
138 while (*name) {
139 fr_ldap_filter_escape_func(request, buffer, sizeof(buffer), *name++, NULL);
140 filter = talloc_asprintf_append_buffer(filter, "(%s=%s)", inst->group.obj_name_attr, buffer);
141
142 group_ctx->name_cnt++;
143 }
144 filter = talloc_asprintf_append_buffer(filter, "%s%s",
145 inst->group.obj_filter ? ")" : "",
146 group_ctx->group_name[0] && group_ctx->group_name[1] ? ")" : "");
147
148 return fr_ldap_trunk_search(group_ctx, &group_ctx->query, request, group_ctx->ttrunk,
149 group_ctx->base_dn->vb_strvalue, inst->group.obj_scope, filter,
150 null_attrs, NULL, NULL);
151}
152
153/** Process the results of looking up group DNs from names
154 *
155 * @param[out] p_result The result of trying to resolve a group name to a dn.
156 * @param[in] request Current request.
157 * @param[in] uctx Group lookup context.
158 * @return One of the RLM_MODULE_* values.
159 */
161{
162 ldap_group_userobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_ctx_t);
163 fr_ldap_query_t *query = talloc_get_type_abort(group_ctx->query, fr_ldap_query_t);
164 rlm_ldap_t const *inst = group_ctx->inst;
166 unsigned int entry_cnt;
167 LDAPMessage *entry;
168 int ldap_errno;
169 char *dn;
170 fr_pair_t *vp;
171
172 switch (query->ret) {
174 break;
175
178 RDEBUG2("Tried to resolve group name(s) to DNs but got no results");
179 goto finish;
180
181 default:
182 rcode = RLM_MODULE_FAIL;
183 goto finish;
184 }
185
186 entry_cnt = ldap_count_entries(query->ldap_conn->handle, query->result);
187 if (entry_cnt > group_ctx->name_cnt) {
188 REDEBUG("Number of DNs exceeds number of names, group and/or dn should be more restrictive");
189 rcode = RLM_MODULE_INVALID;
190
191 goto finish;
192 }
193
194 if (entry_cnt < group_ctx->name_cnt) {
195 RWDEBUG("Got partial mapping of group names (%i) to DNs (%i), membership information may be incomplete",
196 group_ctx->name_cnt, entry_cnt);
197 }
198
199 entry = ldap_first_entry(query->ldap_conn->handle, query->result);
200 if (!entry) {
201 ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
202 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
203
204 rcode = RLM_MODULE_FAIL;
205 goto finish;
206 }
207
208 do {
209 dn = ldap_get_dn(query->ldap_conn->handle, entry);
210 if (!dn) {
211 ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
212 REDEBUG("Retrieving object DN from entry failed: %s", ldap_err2string(ldap_errno));
213
214 rcode = RLM_MODULE_FAIL;
215 goto finish;
216 }
218
219 RDEBUG2("Got group DN \"%s\"", dn);
220 MEM(vp = fr_pair_afrom_da(group_ctx->list_ctx, inst->group.cache_da));
221 fr_pair_value_bstrndup(vp, dn, strlen(dn), true);
222 fr_pair_append(&group_ctx->groups, vp);
223 ldap_memfree(dn);
224 } while((entry = ldap_next_entry(query->ldap_conn->handle, entry)));
225
226finish:
227 /*
228 * Remove pointer to group name to resolve so we don't
229 * try to do it again
230 */
231 *group_ctx->group_name = NULL;
232 talloc_free(group_ctx->query);
233
234 RETURN_UNLANG_RCODE(rcode);
235}
236
237/** Initiate an LDAP search to turn a group DN into it's name
238 *
239 * Unlike the inverse conversion of a name to a DN, most LDAP directories don't allow filtering by DN,
240 * so we need to search for each DN individually.
241 *
242 * @param[out] p_result The result of trying to resolve a dn to a group name..
243 * @param[in] request Current request.
244 * @param[in] uctx The group resolution context.
245 * @return One of the RLM_MODULE_* values.
246 */
248{
249 ldap_group_userobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_ctx_t);
250 rlm_ldap_t const *inst = group_ctx->inst;
251
252 if (!inst->group.obj_name_attr) {
253 REDEBUG("Told to resolve group DN to name but missing 'group.name_attribute' directive");
255 }
256
257 RDEBUG2("Resolving group DN \"%s\" to group name", *group_ctx->dn);
258
259 return fr_ldap_trunk_search(group_ctx, &group_ctx->query, request, group_ctx->ttrunk, *group_ctx->dn,
260 LDAP_SCOPE_BASE, NULL, group_ctx->attrs, NULL, NULL);
261}
262
263/** Process the results of a group DN -> name lookup.
264 *
265 * The retrieved value is added as a value pair to the
266 * temporary list in the group resolution context.
267 *
268 * @param[out] p_result The result of trying to resolve a dn to a group name.
269 * @param[in] request Current request.
270 * @param[in] uctx The group resolution context.
271 * @return One of the RLM_MODULE_* values.
272 */
274{
275 ldap_group_userobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_ctx_t);
276 fr_ldap_query_t *query = talloc_get_type_abort(group_ctx->query, fr_ldap_query_t);
277 rlm_ldap_t const *inst = group_ctx->inst;
278 LDAPMessage *entry;
279 struct berval value;
280 int ldap_errno;
282 fr_pair_t *vp;
283
284 switch (query->ret) {
286 break;
287
290 REDEBUG("Group DN \"%s\" did not resolve to an object", *group_ctx->dn);
291 rcode = (inst->group.allow_dangling_refs ? RLM_MODULE_NOOP : RLM_MODULE_INVALID);
292 goto finish;
293
294 default:
295 rcode = RLM_MODULE_FAIL;
296 goto finish;
297 }
298
299 entry = ldap_first_entry(query->ldap_conn->handle, query->result);
300 if (!entry) {
301 ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
302 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
303 rcode = RLM_MODULE_INVALID;
304 goto finish;
305 }
306
307 if (fr_ldap_entry_value_find(&value, query->ldap_conn->handle, entry, inst->group.obj_name_attr) <= 0) {
308 REDEBUG("No %s attributes found in object", inst->group.obj_name_attr);
309 rcode = RLM_MODULE_INVALID;
310 goto finish;
311 }
312
313 MEM(vp = fr_pair_afrom_da(group_ctx->list_ctx, inst->group.cache_da));
314 fr_pair_value_bstrndup(vp, value.bv_val, value.bv_len, true);
315 fr_pair_append(&group_ctx->groups, vp);
316 RDEBUG2("Group DN \"%s\" resolves to name \"%pV\"", *group_ctx->dn, &vp->data);
317
318finish:
319 /*
320 * Walk the pointer to the DN being resolved forward
321 * ready for the next resolution.
322 */
323 group_ctx->dn++;
324
325 talloc_free(query);
326
327 RETURN_UNLANG_RCODE(rcode);
328}
329
330/** Move user object group attributes to the control list
331 *
332 * @param p_result The result of adding user object group attributes
333 * @param request Current request.
334 * @param group_ctx Context used to evaluate group attributes
335 * @return RLM_MODULE_OK
336 */
338 ldap_group_userobj_ctx_t *group_ctx)
339{
340 fr_pair_t *vp;
341 fr_pair_list_t *list;
342
343 list = tmpl_list_head(request, request_attr_control);
344 fr_assert(list != NULL);
345
346 RDEBUG2("Adding cacheable user object memberships");
347 RINDENT();
348 if (RDEBUG_ENABLED) {
349 for (vp = fr_pair_list_head(&group_ctx->groups);
350 vp;
351 vp = fr_pair_list_next(&group_ctx->groups, vp)) {
352 RDEBUG2("control.%s += \"%pV\"", group_ctx->inst->group.cache_da->name, &vp->data);
353 }
354 }
355
356 fr_pair_list_append(list, &group_ctx->groups);
357 REXDENT();
358
359 talloc_free(group_ctx);
361}
362
363/** Initiate DN to name and name to DN group lookups
364 *
365 * Called repeatedly until there are no more lookups to perform
366 * or an unresolved lookup causes the module to fail.
367 *
368 * @param p_result The result of the previous expansion.
369 * @param request Current request.
370 * @param uctx The group context being processed.
371 * @return One of the RLM_MODULE_* values.
372 */
374{
375 ldap_group_userobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_ctx_t);
376
377 /*
378 * If we've previously failed to expand, fail the group section
379 */
380 switch (p_result->rcode) {
381 case RLM_MODULE_FAIL:
383 talloc_free(group_ctx);
385 default:
386 break;
387 }
388
389 /*
390 * Are there any DN to resolve to names?
391 * These are resolved one at a time as most directories don't allow for
392 * filters on the DN.
393 */
394 if (*group_ctx->dn) {
396 if (unlang_function_push_with_result(/* both start and resume provide an rcode */p_result, request,
401 group_ctx) < 0) RETURN_UNLANG_FAIL;
403 }
404
405 /*
406 * Are there any names to resolve to DN?
407 */
408 if (*group_ctx->group_name) {
410 if (unlang_function_push_with_result(/* both start and resume provide an rcode */p_result, request,
415 group_ctx) < 0) RETURN_UNLANG_FAIL;
417 }
418
419 /*
420 * Nothing left to resolve, move the resulting attributes to
421 * the control list.
422 */
423 return ldap_cacheable_userobj_store(p_result, request, group_ctx);
424}
425
426/** Convert group membership information into attributes
427 *
428 * This may just be able to parse attribute values in the user object
429 * or it may need to yield to other LDAP searches depending on what was
430 * returned and what is set to be cached.
431 *
432 * @param[out] p_result The result of trying to resolve a dn to a group name.
433 * @param[in] request Current request.
434 * @param[in] autz_ctx LDAP authorization context being processed.
435 * @param[in] attr membership attribute to look for in the entry.
436 * @return One of the RLM_MODULE_* values.
437 */
439 char const *attr)
440{
441 rlm_ldap_t const *inst = autz_ctx->inst;
442 LDAPMessage *entry = autz_ctx->entry;
443 fr_ldap_thread_trunk_t *ttrunk = autz_ctx->ttrunk;
444 ldap_group_userobj_ctx_t *group_ctx;
446 struct berval *value;
447 char **name_p;
448 char **dn_p;
449 fr_pair_t *vp;
450 int is_dn, iter_err = 0, name2dn = 0, dn2name = 0;
451 size_t count = 0, strings_len = 0;
452 bool want_profiles = rlm_ldap_profile_attr_select(inst->group.profile_attr,
453 inst->group.profile_attr_suspend,
454 autz_ctx->access_state) != NULL;
455
456 fr_assert(entry);
457 fr_assert(attr);
458
459 /*
460 * Parse the membership information we got in the initial user query.
461 */
463 autz_ctx->query->result, attr) < 0)) {
464 RPERROR("Failed parsing user object");
466 }
467 if (count == 0) {
468 RDEBUG2("No cacheable group memberships found in user object");
469
471 }
472
473 /*
474 * Extended once for the whole batch, only DN valued
475 * memberships are appended.
476 */
477 if (want_profiles) {
478 if (!autz_ctx->group_dn_list) {
479 MEM(autz_ctx->group_dn_list = talloc_str_list_alloc(autz_ctx, count, strings_len));
480 } else {
482 }
483 }
484
485 /*
486 * Set up context for managing group membership attribute resolution.
487 */
488 MEM(group_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), ldap_group_userobj_ctx_t));
489 group_ctx->inst = inst;
490 group_ctx->ttrunk = ttrunk;
491 group_ctx->base_dn = &autz_ctx->call_env->group_base;
492 group_ctx->list_ctx = tmpl_list_ctx(request, request_attr_control);
493 fr_assert(group_ctx->list_ctx != NULL);
494
495 /*
496 * Set up pointers to entries in arrays of names / DNs to resolve.
497 */
498 name_p = group_ctx->group_name;
499 group_ctx->dn = dn_p = group_ctx->group_dn;
500
501 /*
502 * Temporary list to hold new group VPs, will be merged
503 * once all group info has been gathered/resolved
504 * successfully.
505 */
506 fr_pair_list_init(&group_ctx->groups);
507
508 for (value = fr_ldap_value_iter_init(&iter_err, &iter, fr_ldap_handle_thread_local(), entry, attr);
509 value;
510 value = fr_ldap_value_iter_next(&iter_err, &iter)) {
511 is_dn = fr_ldap_util_is_dn(value->bv_val, value->bv_len);
512
513 /*
514 * Record group object DNs for the later profile search.
515 * Name values are only resolved to DNs for caching, so
516 * profiles on groups referenced by name are not found.
517 */
518 if (want_profiles && is_dn) {
519 MEM(talloc_str_list_append(autz_ctx->group_dn_list, value->bv_val, value->bv_len));
520 }
521
522 if (inst->group.cacheable_dn) {
523 /*
524 * The easy case, we're caching DNs and we got a DN.
525 */
526 if (is_dn) {
527 MEM(vp = fr_pair_afrom_da(group_ctx->list_ctx, inst->group.cache_da));
528 fr_pair_value_bstrndup(vp, value->bv_val, value->bv_len, true);
529 fr_pair_append(&group_ctx->groups, vp);
530 /*
531 * We were told to cache DNs but we got a name, we now need to resolve
532 * this to a DN. Store all the group names in an array so we can do one query.
533 */
534 } else {
535 if (++name2dn > LDAP_MAX_CACHEABLE) {
536 REDEBUG("Too many groups require name to DN resolution");
537 invalid:
539 talloc_free(group_ctx);
541 }
542 *name_p++ = fr_ldap_berval_to_string(group_ctx, value);
543 }
544 }
545
546 if (inst->group.cacheable_name) {
547 /*
548 * The easy case, we're caching names and we got a name.
549 */
550 if (!is_dn) {
551 MEM(vp = fr_pair_afrom_da(group_ctx->list_ctx, inst->group.cache_da));
552 fr_pair_value_bstrndup(vp, value->bv_val, value->bv_len, true);
553 fr_pair_append(&group_ctx->groups, vp);
554 /*
555 * We were told to cache names but we got a DN, we now need to resolve
556 * this to a name. Store group DNs which need resolving to names.
557 */
558 } else {
559 if (++dn2name > LDAP_MAX_CACHEABLE) {
560 REDEBUG("Too many groups require DN to name resolution");
561 goto invalid;
562 }
563 *dn_p++ = fr_ldap_berval_to_string(group_ctx, value);
564 }
565 }
566 }
568 if (unlikely(iter_err < 0)) {
569 RPERROR("Failed parsing user object");
570 talloc_free(group_ctx);
572 }
573
574 /*
575 * We either have group names which need converting to DNs or
576 * DNs which need resolving to names. Push a function which will
577 * do the resolution.
578 */
579 if ((name_p != group_ctx->group_name) || (dn_p != group_ctx->group_dn)) {
580 group_ctx->attrs[0] = inst->group.obj_name_attr;
581 if (unlang_function_push_with_result(p_result, request,
583 NULL,
586 group_ctx) < 0) {
587 talloc_free(group_ctx);
589 }
591 }
592
593 /*
594 * No additional queries needed, just process the context to
595 * move any generated pairs into the correct list.
596 */
597 return ldap_cacheable_userobj_store(p_result, request, group_ctx);
598}
599
600/** Initiate an LDAP search for group membership looking at the group objects
601 *
602 * @param[out] p_result Result of submitting LDAP search
603 * @param[in] request Current request.
604 * @param[in] uctx Group lookup context.
605 * @return One of the RLM_MODULE_* values.
606 */
608{
609 ldap_group_groupobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_groupobj_ctx_t);
610 rlm_ldap_t const *inst = group_ctx->inst;
611 fr_value_box_t *filter;
612
613 filter = fr_value_box_list_head(&group_ctx->expanded_filter);
614
615 if (!filter || filter->type != FR_TYPE_STRING) RETURN_UNLANG_FAIL;
616
617 group_ctx->attrs[0] = inst->group.obj_name_attr;
618 return fr_ldap_trunk_search(group_ctx, &group_ctx->query, request, group_ctx->ttrunk,
619 group_ctx->base_dn->vb_strvalue, inst->group.obj_scope,
620 filter->vb_strvalue, group_ctx->attrs, NULL, NULL);
621}
622
623/** Cancel a pending group object lookup.
624 *
625 */
626static void ldap_group_groupobj_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
627{
628 ldap_group_groupobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_groupobj_ctx_t);
629
630 /*
631 * If the query is not in flight, just return
632 */
633 if (!group_ctx->query || !group_ctx->query->treq) return;
634
636}
637
638/** Process the results of a group object lookup.
639 *
640 * @param[out] p_result Result of processing group lookup.
641 * @param[in] request Current request.
642 * @param[in] uctx Group lookup context.
643 * @return One of the RLM_MODULE_* values.
644 */
646{
647 ldap_group_groupobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_groupobj_ctx_t);
648 ldap_autz_ctx_t *autz_ctx = talloc_get_type_abort(group_ctx->uctx, ldap_autz_ctx_t);
649 rlm_ldap_t const *inst = group_ctx->inst;
650 fr_ldap_query_t *query = group_ctx->query;
652 LDAPMessage *entry;
653 int ldap_errno;
654 char *dn;
655 fr_pair_t *vp;
656 bool want_profiles = rlm_ldap_profile_attr_select(inst->group.profile_attr,
657 inst->group.profile_attr_suspend,
658 autz_ctx->access_state) != NULL;
659
660 switch (query->ret) {
661 case LDAP_SUCCESS:
662 break;
663
666 RDEBUG2("No cacheable group memberships found in group objects");
667 rcode = RLM_MODULE_NOTFOUND;
668 goto finish;
669
670 default:
671 rcode = RLM_MODULE_FAIL;
672 goto finish;
673 }
674
675 entry = ldap_first_entry(query->ldap_conn->handle, query->result);
676 if (!entry) {
677 ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
678 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
679
680 goto finish;
681 }
682
683 if (want_profiles) {
684 size_t entry_cnt = (size_t)ldap_count_entries(query->ldap_conn->handle, query->result);
685
686 if (!autz_ctx->group_dn_list) {
687 MEM(autz_ctx->group_dn_list = talloc_str_list_alloc(autz_ctx, entry_cnt, 0));
688 } else {
689 MEM(talloc_str_list_realloc(autz_ctx->group_dn_list, entry_cnt) == 0);
690 }
691 }
692
693 RDEBUG2("Adding cacheable group object memberships");
694 do {
695 if (inst->group.cacheable_dn || want_profiles) {
696 dn = ldap_get_dn(query->ldap_conn->handle, entry);
697 if (!dn) {
698 ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
699 REDEBUG("Retrieving object DN from entry failed: %s", ldap_err2string(ldap_errno));
700
701 goto finish;
702 }
704
705 if (want_profiles) {
706 MEM(talloc_str_list_append(autz_ctx->group_dn_list, dn, strlen(dn)));
707 }
708
709 if (inst->group.cacheable_dn) {
710 MEM(pair_append_control(&vp, inst->group.cache_da) == 0);
711 fr_pair_value_strdup(vp, dn, false);
712
713 RINDENT();
714 RDEBUG2("control.%pP", vp);
715 REXDENT();
716 }
717 ldap_memfree(dn);
718 }
719
720 if (inst->group.cacheable_name) {
721 struct berval value;
722
723 if (fr_ldap_entry_value_find(&value, query->ldap_conn->handle, entry,
724 inst->group.obj_name_attr) <= 0) continue;
725
726 MEM(pair_append_control(&vp, inst->group.cache_da) == 0);
727 fr_pair_value_bstrndup(vp, value.bv_val, value.bv_len, true);
728
729 RINDENT();
730 RDEBUG2("control.%pP", vp);
731 REXDENT();
732 }
733 } while ((entry = ldap_next_entry(query->ldap_conn->handle, entry)));
734
735finish:
736 talloc_free(group_ctx);
737
738 RETURN_UNLANG_RCODE(rcode);
739}
740
741/** Convert group membership information into attributes
742 *
743 * @param[out] p_result The result of trying to resolve a dn to a group name.
744 * @param[in] request Current request.
745 * @param[in] autz_ctx Authentication context being processed.
746 * @return One of the RLM_MODULE_* values.
747 */
749{
750 rlm_ldap_t const *inst = autz_ctx->inst;
751 ldap_group_groupobj_ctx_t *group_ctx;
752
753 if (!inst->group.obj_membership_filter) {
754 RDEBUG2("Skipping caching group objects as directive 'group.membership_filter' is not set");
756 }
757
758 if (autz_ctx->call_env->group_base.type != FR_TYPE_STRING) {
759 REDEBUG("Missing group base_dn");
761 }
762
763 MEM(group_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), ldap_group_groupobj_ctx_t));
764 group_ctx->inst = inst;
765 group_ctx->ttrunk = autz_ctx->ttrunk;
766 group_ctx->base_dn = &autz_ctx->call_env->group_base;
767 group_ctx->uctx = autz_ctx;
768 fr_value_box_list_init(&group_ctx->expanded_filter);
769
771 request,
776 group_ctx) < 0) {
777 error:
778 talloc_free(group_ctx);
780 }
781
782 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;
783
785}
786
787/** Context used when searching for profiles in the user's group objects
788 *
789 */
790typedef struct {
791 ldap_autz_ctx_t *autz_ctx; //!< Authorization context profile DNs are harvested into.
792 char const *profile_attr; //!< Attribute holding profile DNs.
793 char const *filter; //!< Filter matching profile bearing group objects.
794 char const *attrs[2]; //!< For retrieving the profile attribute.
795 fr_ldap_query_t *query; //!< Current query retrieving the group objects.
797
798/** Harvest profile DNs from the group objects returned by the profile search
799 *
800 * @param[out] p_result Result of processing the search response.
801 * @param[in] request Current request.
802 * @param[in] uctx Group profile context.
803 * @return One of the RLM_MODULE_* values.
804 */
806{
807 ldap_group_profile_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_profile_ctx_t);
808 ldap_autz_ctx_t *autz_ctx = group_ctx->autz_ctx;
809 fr_ldap_query_t *query = group_ctx->query;
811
812 switch (query->ret) {
813 case LDAP_SUCCESS:
814 break;
815
818 RDEBUG2("No profiles found in group objects");
819 goto finish;
820
821 default:
822 rcode = RLM_MODULE_FAIL;
823 goto finish;
824 }
825
828 query->result, group_ctx->profile_attr, 0);
829 if (unlikely(!autz_ctx->group_profile_dn_list)) {
830 RPERROR("Failed parsing profiles from group objects");
831 rcode = RLM_MODULE_FAIL;
832 }
833
834finish:
835 talloc_free(group_ctx);
836
837 RETURN_UNLANG_RCODE(rcode);
838}
839
840/** Search for profile DNs in the group objects the user is a member of
841 *
842 * Retrieves only the groups which carry the profile attribute, using a
843 * single search matching the collected group DNs, with the profile
844 * attribute presence asserted in the filter.
845 *
846 * @param[out] p_result Result of submitting the search.
847 * @param[in] request Current request.
848 * @param[in] autz_ctx Authorization context, provides the group DNs
849 * and receives the profile DNs.
850 * @return One of the RLM_MODULE_* values.
851 */
853{
854 rlm_ldap_t const *inst = autz_ctx->inst;
855 ldap_group_profile_ctx_t *group_ctx;
856 char const *profile_attr = rlm_ldap_profile_attr_select(inst->group.profile_attr,
857 inst->group.profile_attr_suspend,
858 autz_ctx->access_state);
859 char const *dn_attr;
860 char const *base;
861
862 fr_assert(profile_attr);
863 fr_assert(autz_ctx->group_dn_list);
864
866 if (!base) {
867 RWDEBUG("Skipping group profiles, no naming context contains every group DN");
869 }
870
871 dn_attr = inst->dn_attr;
872 if (!dn_attr) dn_attr = autz_ctx->ttrunk->directory->dn_attr;
873
874 MEM(group_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), ldap_group_profile_ctx_t));
875 group_ctx->autz_ctx = autz_ctx;
876 group_ctx->profile_attr = profile_attr;
877 group_ctx->attrs[0] = profile_attr;
878 group_ctx->filter = fr_ldap_filter_afrom_dn_list(group_ctx, dn_attr,
879 talloc_asprintf(group_ctx, "(%s=*)", profile_attr),
880 autz_ctx->group_dn_list->strings);
881
882 /*
883 * No signal callback, the frame pushed by fr_ldap_trunk_search
884 * owns cancellation of the query.
885 */
886 if (unlang_function_push_with_result(p_result, request,
887 NULL,
889 NULL, 0, UNLANG_SUB_FRAME,
890 group_ctx) < 0) {
891 talloc_free(group_ctx);
893 }
894
895 return fr_ldap_trunk_search(group_ctx, &group_ctx->query, request, autz_ctx->ttrunk,
896 base, LDAP_SCOPE_SUB, group_ctx->filter, group_ctx->attrs, NULL, NULL);
897}
898
899/** Process the results of a group object lookup.
900 *
901 * @param[out] p_result Result of processing group lookup.
902 * @param[in] request Current request.
903 * @param[in] uctx Group lookup context.
904 * @return One of the RLM_MODULE_* values.
905 */
907{
908 ldap_group_groupobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_groupobj_ctx_t);
909 ldap_group_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(group_ctx->uctx, ldap_group_xlat_ctx_t);
910 fr_ldap_query_t *query = group_ctx->query;
912
913 switch (query->ret) {
914 case LDAP_SUCCESS:
915 xlat_ctx->found = true;
916 if (RDEBUG_ENABLED2) {
917 LDAPMessage *entry = NULL;
918 char *dn = NULL;
919 entry = ldap_first_entry(query->ldap_conn->handle, query->result);
920 if (entry) {
921 dn = ldap_get_dn(query->ldap_conn->handle, entry);
922 RDEBUG2("User found in group object \"%pV\"", fr_box_strvalue(dn));
923 ldap_memfree(dn);
924 }
925 }
926 break;
927
930 rcode = RLM_MODULE_NOTFOUND;
931 break;
932
933 default:
934 rcode = RLM_MODULE_FAIL;
935 break;
936 }
937
938 talloc_free(group_ctx);
939 RETURN_UNLANG_RCODE(rcode);
940}
941
942/** Initiate an LDAP search to determine group membership, querying group objects
943 *
944 * Used by LDAP group membership xlat
945 *
946 * @param p_result Current module result code.
947 * @param request Current request.
948 * @param xlat_ctx xlat context being processed.
949 */
952{
953 rlm_ldap_t const *inst = xlat_ctx->inst;
954 ldap_group_groupobj_ctx_t *group_ctx;
955
957 *group_ctx = (ldap_group_groupobj_ctx_t) {
958 .inst = inst,
959 .ttrunk = xlat_ctx->ttrunk,
960 .uctx = xlat_ctx
961 };
962 fr_value_box_list_init(&group_ctx->expanded_filter);
963
964 if (fr_ldap_util_is_dn(xlat_ctx->group->vb_strvalue, xlat_ctx->group->vb_length)) {
965 group_ctx->filter_tmpl = xlat_ctx->env_data->group_filter;
966 group_ctx->base_dn = xlat_ctx->group;
967 } else {
968 char name_filter[LDAP_MAX_FILTER_STR_LEN];
969 char const *filters[] = { name_filter, inst->group.obj_filter, inst->group.obj_membership_filter };
970 tmpl_rules_t t_rules;
971
972 if (!inst->group.obj_name_attr) {
973 REDEBUG("Told to search for group by name, but missing 'group.name_attribute' "
974 "directive");
975 invalid:
976 talloc_free(group_ctx);
978 }
979
980 t_rules = (tmpl_rules_t){
981 .attr = {
982 .dict_def = request->local_dict,
983 .list_def = request_attr_request,
984 },
985 .xlat = {
986 .runtime_el = unlang_interpret_event_list(request),
987 },
988 .at_runtime = true,
989 .escape.box_escape = (fr_value_box_escape_t) {
992 .always_escape = false,
993 },
994 .escape.mode = TMPL_ESCAPE_PRE_CONCAT,
996 .cast = FR_TYPE_STRING,
997 };
998
999 snprintf(name_filter, sizeof(name_filter), "(%s=%s)",
1000 inst->group.obj_name_attr, xlat_ctx->group->vb_strvalue);
1001
1002 if (fr_ldap_filter_to_tmpl(group_ctx, &t_rules, filters, NUM_ELEMENTS(filters),
1003 &group_ctx->filter_tmpl) < 0) goto invalid;
1004
1005 fr_assert(xlat_ctx->env_data);
1006 group_ctx->base_dn = &xlat_ctx->env_data->group_base;
1007 }
1008
1010 request,
1015 group_ctx) < 0) {
1016 error:
1017 talloc_free(group_ctx);
1019 }
1020
1021 if (unlang_tmpl_push(group_ctx, NULL, &group_ctx->expanded_filter, request, group_ctx->filter_tmpl, NULL, UNLANG_SUB_FRAME) < 0) goto error;
1022
1024}
1025
1026/** Initiate resolving a group DN to its name
1027 *
1028 */
1029static unlang_action_t ldap_dn2name_start(unlang_result_t *p_result, request_t *request, void *uctx)
1030{
1031 ldap_group_userobj_dyn_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_dyn_ctx_t);
1033 rlm_ldap_t const *inst = xlat_ctx->inst;
1034
1035 if (!inst->group.obj_name_attr) {
1036 REDEBUG("Told to resolve group DN to name but missing 'group.name_attribute' directive");
1038 }
1039
1040 RDEBUG2("Resolving group DN \"%pV\" to group name", fr_box_strvalue_buffer(group_ctx->lookup_dn));
1041
1042 return fr_ldap_trunk_search(group_ctx, &group_ctx->query, request, xlat_ctx->ttrunk,
1043 group_ctx->lookup_dn, LDAP_SCOPE_BASE, NULL, group_ctx->attrs,
1044 NULL, NULL);
1045}
1046
1047/** Cancel an in-progress DN to name lookup.
1048 *
1049 */
1050static void ldap_dn2name_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
1051{
1052 ldap_group_userobj_dyn_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_dyn_ctx_t);
1053
1054 if (!group_ctx->query || !group_ctx->query->treq) return;
1055
1057}
1058
1059/** Initiate a user lookup to check membership.
1060 *
1061 * Used when the user's DN is already known but cached group membership has not been stored
1062 *
1063 */
1065 request_t *request, void *uctx)
1066{
1067 ldap_group_userobj_dyn_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_dyn_ctx_t);
1068 ldap_group_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(group_ctx->xlat_ctx, ldap_group_xlat_ctx_t);
1069
1070 return fr_ldap_trunk_search(xlat_ctx, &xlat_ctx->query, request, xlat_ctx->ttrunk, xlat_ctx->dn,
1071 LDAP_SCOPE_BASE, NULL, xlat_ctx->attrs, NULL, NULL);
1072}
1073
1074/** Process the results of evaluating a user object when checking group membership
1075 *
1076 * Any information relating to the user's group memberships should be evailable in the group_ctx
1077 * structure before this is called.
1078 */
1080{
1081 ldap_group_userobj_dyn_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_dyn_ctx_t);
1082 ldap_group_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(group_ctx->xlat_ctx, ldap_group_xlat_ctx_t);
1083 rlm_ldap_t const *inst = xlat_ctx->inst;
1084 fr_ldap_query_t *query = xlat_ctx->query;
1085 LDAPMessage *entry;
1086 int ldap_errno;
1087 bool value_is_dn = false;
1088 fr_value_box_t *group = xlat_ctx->group;
1089 char *value_name = NULL;
1090
1091 /*
1092 * If group_ctx->values is not populated, this is the first call
1093 * - extract the returned values if any.
1094 */
1095 if (!group_ctx->values) {
1096 entry = ldap_first_entry(query->ldap_conn->handle, query->result);
1097 if (!entry) {
1098 ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
1099 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
1101 }
1102
1103 group_ctx->values = ldap_get_values_len(query->ldap_conn->handle, entry, inst->group.userobj_membership_attr);
1104 if (!group_ctx->values) {
1105 RDEBUG2("User object contains no group membership information in attribute \"%s\"",
1106 inst->group.userobj_membership_attr);
1108 }
1109
1110 /*
1111 * To avoid re-assessing after each call out to do a DN -> name
1112 * lookup, cache this.
1113 */
1114 group_ctx->count = ldap_count_values_len(group_ctx->values);
1115 }
1116
1117 /*
1118 * Following a call out to do a DN -> name lookup, group_ctx->query will be
1119 * populated - process the results.
1120 */
1121 if (group_ctx->query) {
1122 char *buff;
1123 struct berval name_value;
1124
1125 switch (group_ctx->query->ret) {
1127 break;
1128
1130 case LDAP_RESULT_BAD_DN:
1131 REDEBUG("Group DN \"%pV\" did not resolve to an object",
1132 fr_box_strvalue_buffer(group_ctx->lookup_dn));
1134
1135 default:
1137 }
1138
1139 entry = ldap_first_entry(group_ctx->query->ldap_conn->handle, group_ctx->query->result);
1140 if (!entry) {
1141 ldap_get_option(group_ctx->query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
1142 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
1144 }
1145
1146 if (fr_ldap_entry_value_find(&name_value, group_ctx->query->ldap_conn->handle, entry,
1147 inst->group.obj_name_attr) <= 0) {
1148 REDEBUG("No %s attributes found in object", inst->group.obj_name_attr);
1150 }
1151
1152 MEM(buff = talloc_bstrndup(group_ctx, name_value.bv_val, name_value.bv_len));
1153 RDEBUG2("Group DN \"%pV\" resolves to name \"%pV\"", fr_box_strvalue_buffer(group_ctx->lookup_dn),
1154 fr_box_strvalue_len(name_value.bv_val, name_value.bv_len));
1155
1156 if (group_ctx->resolving_value) {
1157 value_name = buff;
1158 } else {
1159 group_ctx->group_name = buff;
1160 }
1161 }
1162
1163 /*
1164 * Loop over the list of groups the user is a member of, looking for a match.
1165 */
1166 while (group_ctx->value_no < group_ctx->count) {
1167 struct berval *value = group_ctx->values[group_ctx->value_no];
1168
1169 /*
1170 * We have come back from resolving a membership DN to its name,
1171 * compare to the provided name.
1172 */
1173 if (value_name && group_ctx->resolving_value) {
1174 if (((talloc_strlen(value_name)) == group->vb_length) &&
1175 (memcmp(group->vb_strvalue, value_name, group->vb_length) == 0)) {
1176 RDEBUG2("User found in group \"%pV\". Comparison between membership: name "
1177 "(resolved from DN \"%pV\"), check: name", group,
1178 fr_box_strvalue_buffer(group_ctx->lookup_dn));
1179 talloc_free(value_name);
1180 goto found;
1181 }
1182 talloc_const_free(group_ctx->lookup_dn);
1183 TALLOC_FREE(value_name);
1184 group_ctx->resolving_value = false;
1185 group_ctx->value_no++;
1186 continue;
1187 }
1188
1189 value_is_dn = fr_ldap_util_is_dn(value->bv_val, value->bv_len);
1190
1191 RDEBUG2("Processing %s value \"%pV\" as a %s", inst->group.userobj_membership_attr,
1192 fr_box_strvalue_len(value->bv_val, value->bv_len),
1193 value_is_dn ? "DN" : "group name");
1194
1195 /*
1196 * Both literal group names, do case sensitive comparison
1197 */
1198 if (!xlat_ctx->group_is_dn && !value_is_dn) {
1199 if ((group->vb_length == value->bv_len) &&
1200 (memcmp(value->bv_val, group->vb_strvalue, value->bv_len) == 0)) {
1201 RDEBUG2("User found in group \"%pV\". Comparison between membership: name, check: name",
1202 group);
1203 goto found;
1204 }
1205 group_ctx->value_no++;
1206 continue;
1207 }
1208
1209 /*
1210 * Both DNs, do case insensitive, binary safe comparison
1211 */
1212 if (xlat_ctx->group_is_dn && value_is_dn) {
1213 if (fr_ldap_berval_strncasecmp(value, group->vb_strvalue, group->vb_length) == 0) {
1214 RDEBUG2("User found in group DN \"%pV\". "
1215 "Comparison between membership: dn, check: dn", group);
1216 goto found;
1217 }
1218 group_ctx->value_no++;
1219 continue;
1220 }
1221
1222 /*
1223 * If the value is not a DN, and the name we were given is a dn
1224 * convert the value to a DN and do a comparison.
1225 */
1226 if (!value_is_dn && xlat_ctx->group_is_dn) {
1227 /*
1228 * So we only do the DN -> name lookup once, regardless of how many
1229 * group values we have to check, the resolved name is put in group_ctx->group_name
1230 */
1231 if (!group_ctx->group_name) {
1232 group_ctx->lookup_dn = group->vb_strvalue;
1233
1235
1236 /* Need to push this for the custom cancellation function */
1237 return unlang_function_push_with_result(p_result,
1238 request,
1240 NULL,
1243 group_ctx);
1244 }
1245
1246 if (((talloc_strlen(group_ctx->group_name)) == value->bv_len) &&
1247 (memcmp(value->bv_val, group_ctx->group_name, value->bv_len) == 0)) {
1248 RDEBUG2("User found in group \"%pV\". Comparison between membership: "
1249 "name, check: name (resolved from DN \"%pV\")",
1250 fr_box_strvalue_len(value->bv_val, value->bv_len), group);
1251 goto found;
1252 }
1253 group_ctx->value_no++;
1254 continue;
1255 }
1256
1257 /*
1258 * We have a value which is a DN, and a check item which specifies the name of a group,
1259 * convert the value to a name so we can do a comparison.
1260 */
1261 if (value_is_dn && !xlat_ctx->group_is_dn) {
1262 group_ctx->lookup_dn = fr_ldap_berval_to_string(group_ctx, value);
1263 group_ctx->resolving_value = true;
1264
1266
1267 /* Need to push this for the custom cancellation function */
1268 return unlang_function_push_with_result(p_result,
1269 request,
1271 NULL,
1273 UNLANG_SUB_FRAME, group_ctx);
1274 }
1275
1276 fr_assert(0);
1277 }
1279
1280found:
1281 xlat_ctx->found = true;
1283}
1284
1285/** Ensure retrieved LDAP values are cleared up
1286 *
1287 */
1289{
1290 if (group_ctx->values) ldap_value_free_len(group_ctx->values);
1291 return 0;
1292}
1293
1294/** Query the LDAP directory to check if a user object is a member of a group
1295 *
1296 * @param[out] p_result Result of calling the module.
1297 * @param[in] request Current request.
1298 * @param[in] xlat_ctx Context of the xlat being evaluated.
1299 */
1302{
1303 rlm_ldap_t const *inst = xlat_ctx->inst;
1305
1307 talloc_set_destructor(group_ctx, userobj_dyn_free);
1308
1309 *group_ctx = (ldap_group_userobj_dyn_ctx_t) {
1310 .xlat_ctx = xlat_ctx,
1311 .attrs = { inst->group.obj_name_attr, NULL }
1312 };
1313
1314 RDEBUG2("Checking user object's %s attributes", inst->group.userobj_membership_attr);
1315
1316 /*
1317 * If a previous query was required to find the user DN, that will have
1318 * retrieved the user object membership attribute and the resulting values
1319 * can be checked.
1320 * If not then a query is needed to retrieve the user object.
1321 */
1323 request,
1324 xlat_ctx->query ? NULL : ldap_check_userobj_start,
1326 ldap_group_userobj_cancel, ~FR_SIGNAL_CANCEL,
1328 group_ctx) < 0) {
1329 talloc_free(group_ctx);
1331 }
1332
1334}
1335
1336/** Check group membership attributes to see if a user is a member.
1337 *
1338 * @param[out] p_result Result of calling the module.
1339 * @param[in] inst rlm_ldap configuration.
1340 * @param[in] request Current request.
1341 * @param[in] check vb containing the group value (name or dn).
1342 */
1344 rlm_ldap_t const *inst, request_t *request, fr_value_box_t const *check)
1345{
1346 fr_pair_t *vp;
1347 int ret;
1348 fr_dcursor_t cursor;
1349
1350 /*
1351 * We return RLM_MODULE_INVALID here as an indication
1352 * the caller should try a dynamic group lookup instead.
1353 */
1354 vp = fr_pair_dcursor_by_da_init(&cursor, &request->control_pairs, inst->group.cache_da);
1356
1357 for (vp = fr_dcursor_current(&cursor);
1358 vp;
1359 vp = fr_dcursor_next(&cursor)) {
1360 ret = fr_value_box_cmp_op(T_OP_CMP_EQ, &vp->data, check);
1361 if (ret == 1) {
1362 RDEBUG2("User found. Matched cached membership");
1364 }
1365
1366 if (ret < -1) RETURN_UNLANG_FAIL;
1367 }
1368
1369 RDEBUG2("Cached membership not found");
1370
1372}
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:499
#define RCSID(id)
Definition build.h:512
#define unlikely(_x)
Definition build.h:407
#define UNUSED
Definition build.h:336
#define NUM_ELEMENTS(_t)
Definition build.h:358
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_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:1064
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:748
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:607
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:794
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:950
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:1288
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:1079
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:438
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:373
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:1300
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:645
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:337
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:160
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:793
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:273
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:1029
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:805
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:626
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:906
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:1050
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:852
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:1343
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:247
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:791
char const * profile_attr
Attribute holding profile DNs.
Definition groups.c:792
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:795
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:790
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:2047
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
Definition interpret.c:2423
#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:556
size_t fr_ldap_util_normalise_dn(char *out, char const *in)
Normalise escape sequences in a DN.
Definition util.c:827
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:948
#define LDAP_MAX_FILTER_STR_LEN
Maximum length of an xlat expanded filter.
Definition base.h:110
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:748
int fr_ldap_filter_box_escape(fr_value_box_t *vb, UNUSED void *uctx)
Definition util.c:183
void fr_ldap_value_iter_done(fr_ldap_value_iter_t *iter)
Release value iteration state.
Definition util.c:507
bool fr_ldap_util_is_dn(char const *in, size_t inlen)
Check whether a string looks like a DN.
Definition util.c:275
size_t fr_ldap_filter_escape_func(UNUSED request_t *request, char *out, size_t outlen, char const *in, UNUSED void *arg))
Escape a string for use as an RFC 4515 filter assertion value.
Definition util.c:155
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:523
LDAP * handle
libldap handle.
Definition base.h:343
fr_ldap_result_code_t ret
Result code.
Definition base.h:473
#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:459
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:653
char * fr_ldap_berval_to_string(TALLOC_CTX *ctx, struct berval const *in)
Convert a berval to a talloced string.
Definition util.c:778
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:696
fr_ldap_connection_t * ldap_conn
LDAP connection this query is running on.
Definition base.h:460
@ LDAP_RESULT_SUCCESS
Successfully got LDAP results.
Definition base.h:191
@ LDAP_RESULT_NO_RESULT
No results returned.
Definition base.h:195
@ LDAP_RESULT_BAD_DN
The requested DN does not exist.
Definition base.h:194
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:680
LDAPMessage * result
Head of LDAP results list.
Definition base.h:471
fr_ldap_directory_t * directory
The type of directory we're connected to.
Definition base.h:407
char const * dn_attr
Attribute to match an entry's DN in a search filter.
Definition base.h:214
#define LDAP_MAX_GROUP_NAME_LEN
Maximum name of a group name.
Definition base.h:108
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:909
LDAP query structure.
Definition base.h:425
Thread LDAP trunk structure.
Definition base.h:402
State of an in place iteration over an attribute's values.
Definition base.h:968
LDAP * fr_ldap_handle_thread_local(void)
Get a thread local dummy LDAP handle.
Definition base.c:1130
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:718
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition log.h:455
#define RWDEBUG(fmt,...)
Definition log.h:373
#define RPERROR(fmt,...)
Definition log.h:314
#define RINDENT()
Indent R* messages by one level.
Definition log.h:442
@ 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:2663
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:1352
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:290
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:2812
#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
struct rlm_ldap_t::@177 group
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
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
#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
return count
Definition module.c:155
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_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition talloc.c:546
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
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:2199
#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:639
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:993
#define fr_box_strvalue_buffer(_val)
Definition value.h:312
#define fr_box_strvalue_len(_val, _len)
Definition value.h:309
#define fr_box_strvalue(_val)
Definition value.h:308
uintptr_t fr_value_box_safe_for_t
Escaping that's been applied to a value box.
Definition value.h:162
static TALLOC_CTX * xlat_ctx