The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
groups.c
Go to the documentation of this file.
1/*
2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 12a5e576be06a1f30bd1d54f1fa677c80592a166 $
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: 12a5e576be06a1f30bd1d54f1fa677c80592a166 $")
28
30
31#include <freeradius-devel/util/debug.h>
32#include <freeradius-devel/server/rcode.h>
33#include <freeradius-devel/unlang/action.h>
34
35#define LOG_PREFIX "rlm_ldap groups"
36
37#include "rlm_ldap.h"
38
39static char const *null_attrs[] = { NULL };
40
41/** Context to use when resolving group membership from the user object.
42 *
43 */
44typedef struct {
45 rlm_ldap_t const *inst; //!< Module instance.
46 fr_value_box_t *base_dn; //!< The base DN to search for groups in.
47 fr_ldap_thread_trunk_t *ttrunk; //!< Trunk on which to perform additional queries.
48 fr_pair_list_t groups; //!< Temporary list to hold pairs.
49 TALLOC_CTX *list_ctx; //!< In which to allocate pairs.
50 char *group_name[LDAP_MAX_CACHEABLE + 1]; //!< List of group names which need resolving.
51 unsigned int name_cnt; //!< How many names need resolving.
52 char *group_dn[LDAP_MAX_CACHEABLE + 1]; //!< List of group DNs which need resolving.
53 char **dn; //!< Current DN being resolved.
54 char const *attrs[2]; //!< For resolving name from DN.
55 fr_ldap_query_t *query; //!< Current query performing group resolution.
57
58/** Context to use when looking up group membership using group objects.
59 *
60 */
61typedef struct {
62 rlm_ldap_t const *inst; //!< Module instance.
63 fr_value_box_t *base_dn; //!< The base DN to search for groups in.
64 fr_ldap_thread_trunk_t *ttrunk; //!< Trunk on which to perform additional queries.
65 tmpl_t *filter_tmpl; //!< Tmpl to expand into LDAP filter.
66 fr_value_box_list_t expanded_filter; //!< Values produced by expanding filter xlat.
67 char const *attrs[2]; //!< For retrieving the group name.
68 fr_ldap_query_t *query; //!< Current query performing group lookup.
69 void *uctx; //!< Optional context for use in results parsing.
71
72/** Context to use when evaluating group membership from the user object in an xlat
73 *
74 */
75typedef struct {
76 ldap_group_xlat_ctx_t *xlat_ctx; //!< Xlat context being evaluated.
77 char const *attrs[2]; //!< For retrieving the group name.
78 struct berval **values; //!< Values of the membership attribute to check.
79 int count; //!< How many entries there are in values.
80 int value_no; //!< The current entry in values being processed.
81 char const *lookup_dn; //!< The DN currently being looked up, when resolving DN to name.
82 char *group_name; //!< Result of resolving the provided group DN as to a name.
83 fr_ldap_query_t *query; //!< Current query doing a DN to name resolution.
84 bool resolving_value; //!< Is the current query resolving a DN from values.
86
87/** Cancel a pending group lookup query
88 *
89 */
90static void ldap_group_userobj_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
91{
92 ldap_group_userobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_ctx_t);
93
94 /*
95 * If the query is not in flight, just return.
96 */
97 if (!group_ctx->query || !(group_ctx->query->treq)) return;
98
100}
101
102/** Convert multiple group names into a DNs
103 *
104 * Given an array of group names, builds a filter matching all names, then retrieves all group objects
105 * and stores the DN associated with each group object.
106 *
107 * @param[out] p_result The result of trying to resolve a group name to a dn.
108 * @param[in] request Current request.
109 * @param[in] uctx Group lookup context.
110 * @return One of the RLM_MODULE_* values.
111 */
113{
114 ldap_group_userobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_ctx_t);
115 rlm_ldap_t const *inst = group_ctx->inst;
116 char **name = group_ctx->group_name;
118 char *filter;
119
120 if (!inst->group.obj_name_attr) {
121 REDEBUG("Told to convert group names to DNs but missing 'group.name_attribute' directive");
123 }
124 if (group_ctx->base_dn->type != FR_TYPE_STRING) {
125 REDEBUG("Missing group base_dn");
127 }
128
129 RDEBUG2("Converting group name(s) to group DN(s)");
130
131 /*
132 * It'll probably only save a few ms in network latency, but it means we can send a query
133 * for the entire group list at once.
134 */
135 filter = talloc_typed_asprintf(group_ctx, "%s%s%s",
136 inst->group.obj_filter ? "(&" : "",
137 inst->group.obj_filter ? inst->group.obj_filter : "",
138 group_ctx->group_name[0] && group_ctx->group_name[1] ? "(|" : "");
139 while (*name) {
140 fr_ldap_uri_escape_func(request, buffer, sizeof(buffer), *name++, NULL);
141 filter = talloc_asprintf_append_buffer(filter, "(%s=%s)", inst->group.obj_name_attr, buffer);
142
143 group_ctx->name_cnt++;
144 }
145 filter = talloc_asprintf_append_buffer(filter, "%s%s",
146 inst->group.obj_filter ? ")" : "",
147 group_ctx->group_name[0] && group_ctx->group_name[1] ? ")" : "");
148
149 return fr_ldap_trunk_search(group_ctx, &group_ctx->query, request, group_ctx->ttrunk,
150 group_ctx->base_dn->vb_strvalue, inst->group.obj_scope, filter,
151 null_attrs, NULL, NULL);
152}
153
154/** Process the results of looking up group DNs from names
155 *
156 * @param[out] p_result The result of trying to resolve a group name to a dn.
157 * @param[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 **values = NULL;
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 values = ldap_get_values_len(query->ldap_conn->handle, entry, inst->group.obj_name_attr);
309 if (!values) {
310 REDEBUG("No %s attributes found in object", inst->group.obj_name_attr);
311 rcode = RLM_MODULE_INVALID;
312 goto finish;
313 }
314
315 MEM(vp = fr_pair_afrom_da(group_ctx->list_ctx, inst->group.cache_da));
316 fr_pair_value_bstrndup(vp, values[0]->bv_val, values[0]->bv_len, true);
317 fr_pair_append(&group_ctx->groups, vp);
318 RDEBUG2("Group DN \"%s\" resolves to name \"%pV\"", *group_ctx->dn, &vp->data);
319
320finish:
321 /*
322 * Walk the pointer to the DN being resolved forward
323 * ready for the next resolution.
324 */
325 group_ctx->dn++;
326
327 if (values) ldap_value_free_len(values);
328 talloc_free(query);
329
330 RETURN_UNLANG_RCODE(rcode);
331}
332
333/** Move user object group attributes to the control list
334 *
335 * @param p_result The result of adding user object group attributes
336 * @param request Current request.
337 * @param group_ctx Context used to evaluate group attributes
338 * @return RLM_MODULE_OK
339 */
341 ldap_group_userobj_ctx_t *group_ctx)
342{
343 fr_pair_t *vp;
344 fr_pair_list_t *list;
345
346 list = tmpl_list_head(request, request_attr_control);
347 fr_assert(list != NULL);
348
349 RDEBUG2("Adding cacheable user object memberships");
350 RINDENT();
351 if (RDEBUG_ENABLED) {
352 for (vp = fr_pair_list_head(&group_ctx->groups);
353 vp;
354 vp = fr_pair_list_next(&group_ctx->groups, vp)) {
355 RDEBUG2("control.%s += \"%pV\"", group_ctx->inst->group.cache_da->name, &vp->data);
356 }
357 }
358
359 fr_pair_list_append(list, &group_ctx->groups);
360 REXDENT();
361
362 talloc_free(group_ctx);
364}
365
366/** Initiate DN to name and name to DN group lookups
367 *
368 * Called repeatedly until there are no more lookups to perform
369 * or an unresolved lookup causes the module to fail.
370 *
371 * @param p_result The result of the previous expansion.
372 * @param request Current request.
373 * @param uctx The group context being processed.
374 * @return One of the RLM_MODULE_* values.
375 */
377{
378 ldap_group_userobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_ctx_t);
379
380 /*
381 * If we've previously failed to expand, fail the group section
382 */
383 switch (p_result->rcode) {
384 case RLM_MODULE_FAIL:
386 talloc_free(group_ctx);
388 default:
389 break;
390 }
391
392 /*
393 * Are there any DN to resolve to names?
394 * These are resolved one at a time as most directories don't allow for
395 * filters on the DN.
396 */
397 if (*group_ctx->dn) {
399 if (unlang_function_push_with_result(/* both start and resume provide an rcode */p_result, request,
404 group_ctx) < 0) RETURN_UNLANG_FAIL;
406 }
407
408 /*
409 * Are there any names to resolve to DN?
410 */
411 if (*group_ctx->group_name) {
413 if (unlang_function_push_with_result(/* both start and resume provide an rcode */p_result, request,
418 group_ctx) < 0) RETURN_UNLANG_FAIL;
420 }
421
422 /*
423 * Nothing left to resolve, move the resulting attributes to
424 * the control list.
425 */
426 return ldap_cacheable_userobj_store(p_result, request, group_ctx);
427}
428
429/** Convert group membership information into attributes
430 *
431 * This may just be able to parse attribute values in the user object
432 * or it may need to yield to other LDAP searches depending on what was
433 * returned and what is set to be cached.
434 *
435 * @param[out] p_result The result of trying to resolve a dn to a group name.
436 * @param[in] request Current request.
437 * @param[in] autz_ctx LDAP authorization context being processed.
438 * @param[in] attr membership attribute to look for in the entry.
439 * @return One of the RLM_MODULE_* values.
440 */
442 char const *attr)
443{
444 rlm_ldap_t const *inst = autz_ctx->inst;
445 LDAPMessage *entry = autz_ctx->entry;
446 fr_ldap_thread_trunk_t *ttrunk = autz_ctx->ttrunk;
447 ldap_group_userobj_ctx_t *group_ctx;
448 struct berval **values;
449 char **name_p;
450 char **dn_p;
451 fr_pair_t *vp;
452 int is_dn, i, count, name2dn = 0, dn2name = 0;
453
454 fr_assert(entry);
455 fr_assert(attr);
456
457 /*
458 * Parse the membership information we got in the initial user query.
459 */
460 values = ldap_get_values_len(fr_ldap_handle_thread_local(), entry, attr);
461 if (!values) {
462 RDEBUG2("No cacheable group memberships found in user object");
463
465 }
466 count = ldap_count_values_len(values);
467
468 /*
469 * Set up context for managing group membership attribute resolution.
470 */
471 MEM(group_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), ldap_group_userobj_ctx_t));
472 group_ctx->inst = inst;
473 group_ctx->ttrunk = ttrunk;
474 group_ctx->base_dn = &autz_ctx->call_env->group_base;
475 group_ctx->list_ctx = tmpl_list_ctx(request, request_attr_control);
476 fr_assert(group_ctx->list_ctx != NULL);
477
478 /*
479 * Set up pointers to entries in arrays of names / DNs to resolve.
480 */
481 name_p = group_ctx->group_name;
482 group_ctx->dn = dn_p = group_ctx->group_dn;
483
484 /*
485 * Temporary list to hold new group VPs, will be merged
486 * once all group info has been gathered/resolved
487 * successfully.
488 */
489 fr_pair_list_init(&group_ctx->groups);
490
491 for (i = 0; (i < count); i++) {
492 is_dn = fr_ldap_util_is_dn(values[i]->bv_val, values[i]->bv_len);
493
494 if (inst->group.cacheable_dn) {
495 /*
496 * The easy case, we're caching DNs and we got a DN.
497 */
498 if (is_dn) {
499 MEM(vp = fr_pair_afrom_da(group_ctx->list_ctx, inst->group.cache_da));
500 fr_pair_value_bstrndup(vp, values[i]->bv_val, values[i]->bv_len, true);
501 fr_pair_append(&group_ctx->groups, vp);
502 /*
503 * We were told to cache DNs but we got a name, we now need to resolve
504 * this to a DN. Store all the group names in an array so we can do one query.
505 */
506 } else {
507 if (++name2dn > LDAP_MAX_CACHEABLE) {
508 REDEBUG("Too many groups require name to DN resolution");
509 invalid:
510 ldap_value_free_len(values);
511 talloc_free(group_ctx);
513 }
514 *name_p++ = fr_ldap_berval_to_string(group_ctx, values[i]);
515 }
516 }
517
518 if (inst->group.cacheable_name) {
519 /*
520 * The easy case, we're caching names and we got a name.
521 */
522 if (!is_dn) {
523 MEM(vp = fr_pair_afrom_da(group_ctx->list_ctx, inst->group.cache_da));
524 fr_pair_value_bstrndup(vp, values[i]->bv_val, values[i]->bv_len, true);
525 fr_pair_append(&group_ctx->groups, vp);
526 /*
527 * We were told to cache names but we got a DN, we now need to resolve
528 * this to a name. Store group DNs which need resolving to names.
529 */
530 } else {
531 if (++dn2name > LDAP_MAX_CACHEABLE) {
532 REDEBUG("Too many groups require DN to name resolution");
533 goto invalid;
534 }
535 *dn_p++ = fr_ldap_berval_to_string(group_ctx, values[i]);
536 }
537 }
538 }
539
540 ldap_value_free_len(values);
541
542 /*
543 * We either have group names which need converting to DNs or
544 * DNs which need resolving to names. Push a function which will
545 * do the resolution.
546 */
547 if ((name_p != group_ctx->group_name) || (dn_p != group_ctx->group_dn)) {
548 group_ctx->attrs[0] = inst->group.obj_name_attr;
549 if (unlang_function_push_with_result(p_result, request,
551 NULL,
554 group_ctx) < 0) {
555 talloc_free(group_ctx);
557 }
559 }
560
561 /*
562 * No additional queries needed, just process the context to
563 * move any generated pairs into the correct list.
564 */
565 return ldap_cacheable_userobj_store(p_result, request, group_ctx);
566}
567
568/** Initiate an LDAP search for group membership looking at the group objects
569 *
570 * @param[out] p_result Result of submitting LDAP search
571 * @param[in] request Current request.
572 * @param[in] uctx Group lookup context.
573 * @return One of the RLM_MODULE_* values.
574 */
576{
577 ldap_group_groupobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_groupobj_ctx_t);
578 rlm_ldap_t const *inst = group_ctx->inst;
579 fr_value_box_t *filter;
580
581 filter = fr_value_box_list_head(&group_ctx->expanded_filter);
582
583 if (filter->type != FR_TYPE_STRING) RETURN_UNLANG_FAIL;
584
585 group_ctx->attrs[0] = inst->group.obj_name_attr;
586 return fr_ldap_trunk_search(group_ctx, &group_ctx->query, request, group_ctx->ttrunk,
587 group_ctx->base_dn->vb_strvalue, inst->group.obj_scope,
588 filter->vb_strvalue, group_ctx->attrs, NULL, NULL);
589}
590
591/** Cancel a pending group object lookup.
592 *
593 */
594static void ldap_group_groupobj_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
595{
596 ldap_group_groupobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_groupobj_ctx_t);
597
598 /*
599 * If the query is not in flight, just return
600 */
601 if (!group_ctx->query || !group_ctx->query->treq) return;
602
604}
605
606/** Process the results of a group object lookup.
607 *
608 * @param[out] p_result Result of processing group lookup.
609 * @param[in] request Current request.
610 * @param[in] uctx Group lookup context.
611 * @return One of the RLM_MODULE_* values.
612 */
614{
615 ldap_group_groupobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_groupobj_ctx_t);
616 rlm_ldap_t const *inst = group_ctx->inst;
617 fr_ldap_query_t *query = group_ctx->query;
619 LDAPMessage *entry;
620 int ldap_errno;
621 char *dn;
622 fr_pair_t *vp;
623
624 switch (query->ret) {
625 case LDAP_SUCCESS:
626 break;
627
630 RDEBUG2("No cacheable group memberships found in group objects");
631 rcode = RLM_MODULE_NOTFOUND;
632 goto finish;
633
634 default:
635 rcode = RLM_MODULE_FAIL;
636 goto finish;
637 }
638
639 entry = ldap_first_entry(query->ldap_conn->handle, query->result);
640 if (!entry) {
641 ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
642 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
643
644 goto finish;
645 }
646
647 RDEBUG2("Adding cacheable group object memberships");
648 do {
649 if (inst->group.cacheable_dn) {
650 dn = ldap_get_dn(query->ldap_conn->handle, entry);
651 if (!dn) {
652 ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
653 REDEBUG("Retrieving object DN from entry failed: %s", ldap_err2string(ldap_errno));
654
655 goto finish;
656 }
658
659 MEM(pair_append_control(&vp, inst->group.cache_da) == 0);
660 fr_pair_value_strdup(vp, dn, false);
661
662 RINDENT();
663 RDEBUG2("control.%pP", vp);
664 REXDENT();
665 ldap_memfree(dn);
666 }
667
668 if (inst->group.cacheable_name) {
669 struct berval **values;
670
671 values = ldap_get_values_len(query->ldap_conn->handle, entry, inst->group.obj_name_attr);
672 if (!values) continue;
673
674 MEM(pair_append_control(&vp, inst->group.cache_da) == 0);
675 fr_pair_value_bstrndup(vp, values[0]->bv_val, values[0]->bv_len, true);
676
677 RINDENT();
678 RDEBUG2("control.%pP", vp);
679 REXDENT();
680
681 ldap_value_free_len(values);
682 }
683 } while ((entry = ldap_next_entry(query->ldap_conn->handle, entry)));
684
685finish:
686 talloc_free(group_ctx);
687
688 RETURN_UNLANG_RCODE(rcode);
689}
690
691/** Convert group membership information into attributes
692 *
693 * @param[out] p_result The result of trying to resolve a dn to a group name.
694 * @param[in] request Current request.
695 * @param[in] autz_ctx Authentication context being processed.
696 * @return One of the RLM_MODULE_* values.
697 */
699{
700 rlm_ldap_t const *inst = autz_ctx->inst;
701 ldap_group_groupobj_ctx_t *group_ctx;
702
703 if (!inst->group.obj_membership_filter) {
704 RDEBUG2("Skipping caching group objects as directive 'group.membership_filter' is not set");
706 }
707
708 if (autz_ctx->call_env->group_base.type != FR_TYPE_STRING) {
709 REDEBUG("Missing group base_dn");
711 }
712
713 MEM(group_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), ldap_group_groupobj_ctx_t));
714 group_ctx->inst = inst;
715 group_ctx->ttrunk = autz_ctx->ttrunk;
716 group_ctx->base_dn = &autz_ctx->call_env->group_base;
717 fr_value_box_list_init(&group_ctx->expanded_filter);
718
720 request,
725 group_ctx) < 0) {
726 error:
727 talloc_free(group_ctx);
729 }
730
731 if (unlang_tmpl_push(group_ctx, &group_ctx->expanded_filter, request, autz_ctx->call_env->group_filter, NULL) < 0) goto error;
732
734}
735
736/** Process the results of a group object lookup.
737 *
738 * @param[out] p_result Result of processing group lookup.
739 * @param[in] request Current request.
740 * @param[in] uctx Group lookup context.
741 * @return One of the RLM_MODULE_* values.
742 */
744{
745 ldap_group_groupobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_groupobj_ctx_t);
746 ldap_group_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(group_ctx->uctx, ldap_group_xlat_ctx_t);
747 fr_ldap_query_t *query = group_ctx->query;
749
750 switch (query->ret) {
751 case LDAP_SUCCESS:
752 xlat_ctx->found = true;
753 if (RDEBUG_ENABLED2) {
754 LDAPMessage *entry = NULL;
755 char *dn = NULL;
756 entry = ldap_first_entry(query->ldap_conn->handle, query->result);
757 if (entry) {
758 dn = ldap_get_dn(query->ldap_conn->handle, entry);
759 RDEBUG2("User found in group object \"%pV\"", fr_box_strvalue(dn));
760 ldap_memfree(dn);
761 }
762 }
763 break;
764
767 rcode = RLM_MODULE_NOTFOUND;
768 break;
769
770 default:
771 rcode = RLM_MODULE_FAIL;
772 break;
773 }
774
775 talloc_free(group_ctx);
776 RETURN_UNLANG_RCODE(rcode);
777}
778
779/** Initiate an LDAP search to determine group membership, querying group objects
780 *
781 * Used by LDAP group membership xlat
782 *
783 * @param p_result Current module result code.
784 * @param request Current request.
785 * @param xlat_ctx xlat context being processed.
786 */
789{
790 rlm_ldap_t const *inst = xlat_ctx->inst;
791 ldap_group_groupobj_ctx_t *group_ctx;
792
794 *group_ctx = (ldap_group_groupobj_ctx_t) {
795 .inst = inst,
796 .ttrunk = xlat_ctx->ttrunk,
797 .uctx = xlat_ctx
798 };
799 fr_value_box_list_init(&group_ctx->expanded_filter);
800
801 if (fr_ldap_util_is_dn(xlat_ctx->group->vb_strvalue, xlat_ctx->group->vb_length)) {
802 group_ctx->filter_tmpl = xlat_ctx->env_data->group_filter;
803 group_ctx->base_dn = xlat_ctx->group;
804 } else {
805 char name_filter[LDAP_MAX_FILTER_STR_LEN];
806 char const *filters[] = { name_filter, inst->group.obj_filter, inst->group.obj_membership_filter };
807 tmpl_rules_t t_rules;
808
809 if (!inst->group.obj_name_attr) {
810 REDEBUG("Told to search for group by name, but missing 'group.name_attribute' "
811 "directive");
812 invalid:
813 talloc_free(group_ctx);
815 }
816
817 t_rules = (tmpl_rules_t){
818 .attr = {
819 .dict_def = request->proto_dict,
820 .list_def = request_attr_request,
821 },
822 .xlat = {
823 .runtime_el = unlang_interpret_event_list(request),
824 },
825 .at_runtime = true,
826 .escape.box_escape = (fr_value_box_escape_t) {
827 .func = fr_ldap_box_escape,
829 .always_escape = false,
830 },
831 .escape.mode = TMPL_ESCAPE_PRE_CONCAT,
832 .literals_safe_for = (fr_value_box_safe_for_t)fr_ldap_box_escape,
833 .cast = FR_TYPE_STRING,
834 };
835
836 snprintf(name_filter, sizeof(name_filter), "(%s=%s)",
837 inst->group.obj_name_attr, xlat_ctx->group->vb_strvalue);
838
839 if (fr_ldap_filter_to_tmpl(group_ctx, &t_rules, filters, NUM_ELEMENTS(filters),
840 &group_ctx->filter_tmpl) < 0) goto invalid;
841
842 fr_assert(xlat_ctx->env_data);
843 group_ctx->base_dn = &xlat_ctx->env_data->group_base;
844 }
845
847 request,
852 group_ctx) < 0) {
853 error:
854 talloc_free(group_ctx);
856 }
857
858 if (unlang_tmpl_push(group_ctx, &group_ctx->expanded_filter, request, group_ctx->filter_tmpl, NULL) < 0) goto error;
859
861}
862
863/** Initiate resolving a group DN to its name
864 *
865 */
866static unlang_action_t ldap_dn2name_start(unlang_result_t *p_result, request_t *request, void *uctx)
867{
868 ldap_group_userobj_dyn_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_dyn_ctx_t);
870 rlm_ldap_t const *inst = xlat_ctx->inst;
871
872 if (!inst->group.obj_name_attr) {
873 REDEBUG("Told to resolve group DN to name but missing 'group.name_attribute' directive");
875 }
876
877 RDEBUG2("Resolving group DN \"%pV\" to group name", fr_box_strvalue_buffer(group_ctx->lookup_dn));
878
879 return fr_ldap_trunk_search(group_ctx, &group_ctx->query, request, xlat_ctx->ttrunk,
880 group_ctx->lookup_dn, LDAP_SCOPE_BASE, NULL, group_ctx->attrs,
881 NULL, NULL);
882}
883
884/** Cancel an in-progress DN to name lookup.
885 *
886 */
887static void ldap_dn2name_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
888{
889 ldap_group_userobj_dyn_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_dyn_ctx_t);
890
891 if (!group_ctx->query || !group_ctx->query->treq) return;
892
894}
895
896/** Initiate a user lookup to check membership.
897 *
898 * Used when the user's DN is already known but cached group membership has not been stored
899 *
900 */
902 request_t *request, void *uctx)
903{
904 ldap_group_userobj_dyn_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_dyn_ctx_t);
905 ldap_group_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(group_ctx->xlat_ctx, ldap_group_xlat_ctx_t);
906
907 return fr_ldap_trunk_search(xlat_ctx, &xlat_ctx->query, request, xlat_ctx->ttrunk, xlat_ctx->dn,
908 LDAP_SCOPE_BASE, NULL, xlat_ctx->attrs, NULL, NULL);
909}
910
911/** Process the results of evaluating a user object when checking group membership
912 *
913 * Any information relating to the user's group memberships should be evailable in the group_ctx
914 * structure before this is called.
915 */
917{
918 ldap_group_userobj_dyn_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_userobj_dyn_ctx_t);
919 ldap_group_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(group_ctx->xlat_ctx, ldap_group_xlat_ctx_t);
920 rlm_ldap_t const *inst = xlat_ctx->inst;
921 fr_ldap_query_t *query = xlat_ctx->query;
922 LDAPMessage *entry;
923 int ldap_errno;
924 bool value_is_dn = false;
925 fr_value_box_t *group = xlat_ctx->group;
926 char *value_name = NULL;
927
928 /*
929 * If group_ctx->values is not populated, this is the first call
930 * - extract the returned values if any.
931 */
932 if (!group_ctx->values) {
933 entry = ldap_first_entry(query->ldap_conn->handle, query->result);
934 if (!entry) {
935 ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
936 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
938 }
939
940 group_ctx->values = ldap_get_values_len(query->ldap_conn->handle, entry, inst->group.userobj_membership_attr);
941 if (!group_ctx->values) {
942 RDEBUG2("User object contains no group membership information in attribute \"%s\"",
943 inst->group.userobj_membership_attr);
945 }
946
947 /*
948 * To avoid re-assessing after each call out to do a DN -> name
949 * lookup, cache this.
950 */
951 group_ctx->count = ldap_count_values_len(group_ctx->values);
952 }
953
954 /*
955 * Following a call out to do a DN -> name lookup, group_ctx->query will be
956 * populated - process the results.
957 */
958 if (group_ctx->query) {
959 char *buff;
960 struct berval **values = NULL;
961
962 switch (group_ctx->query->ret) {
964 break;
965
968 REDEBUG("Group DN \"%pV\" did not resolve to an object",
971
972 default:
974 }
975
976 entry = ldap_first_entry(group_ctx->query->ldap_conn->handle, group_ctx->query->result);
977 if (!entry) {
978 ldap_get_option(group_ctx->query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
979 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
981 }
982
983 values = ldap_get_values_len(group_ctx->query->ldap_conn->handle, entry, inst->group.obj_name_attr);
984 if (!values) {
985 REDEBUG("No %s attributes found in object", inst->group.obj_name_attr);
987 }
988
989 MEM(buff = talloc_bstrndup(group_ctx, values[0]->bv_val, values[0]->bv_len));
990 RDEBUG2("Group DN \"%pV\" resolves to name \"%pV\"", fr_box_strvalue_buffer(group_ctx->lookup_dn),
991 fr_box_strvalue_len(values[0]->bv_val, values[0]->bv_len));
992 ldap_value_free_len(values);
993
994 if (group_ctx->resolving_value) {
995 value_name = buff;
996 } else {
997 group_ctx->group_name = buff;
998 }
999 }
1000
1001 /*
1002 * Loop over the list of groups the user is a member of, looking for a match.
1003 */
1004 while (group_ctx->value_no < group_ctx->count) {
1005 struct berval *value = group_ctx->values[group_ctx->value_no];
1006
1007 /*
1008 * We have come back from resolving a membership DN to its name,
1009 * compare to the provided name.
1010 */
1011 if (value_name && group_ctx->resolving_value) {
1012 if (((talloc_array_length(value_name) - 1) == group->vb_length) &&
1013 (memcmp(group->vb_strvalue, value_name, group->vb_length) == 0)) {
1014 RDEBUG2("User found in group \"%pV\". Comparison between membership: name "
1015 "(resolved from DN \"%pV\"), check: name", group,
1016 fr_box_strvalue_buffer(group_ctx->lookup_dn));
1017 talloc_free(value_name);
1018 goto found;
1019 }
1020 talloc_const_free(group_ctx->lookup_dn);
1021 TALLOC_FREE(value_name);
1022 group_ctx->resolving_value = false;
1023 group_ctx->value_no++;
1024 continue;
1025 }
1026
1027 value_is_dn = fr_ldap_util_is_dn(value->bv_val, value->bv_len);
1028
1029 RDEBUG2("Processing %s value \"%pV\" as a %s", inst->group.userobj_membership_attr,
1030 fr_box_strvalue_len(value->bv_val, value->bv_len),
1031 value_is_dn ? "DN" : "group name");
1032
1033 /*
1034 * Both literal group names, do case sensitive comparison
1035 */
1036 if (!xlat_ctx->group_is_dn && !value_is_dn) {
1037 if ((group->vb_length == value->bv_len) &&
1038 (memcmp(value->bv_val, group->vb_strvalue, value->bv_len) == 0)) {
1039 RDEBUG2("User found in group \"%pV\". Comparison between membership: name, check: name",
1040 group);
1041 goto found;
1042 }
1043 group_ctx->value_no++;
1044 continue;
1045 }
1046
1047 /*
1048 * Both DNs, do case insensitive, binary safe comparison
1049 */
1050 if (xlat_ctx->group_is_dn && value_is_dn) {
1051 if (fr_ldap_berval_strncasecmp(value, group->vb_strvalue, group->vb_length) == 0) {
1052 RDEBUG2("User found in group DN \"%pV\". "
1053 "Comparison between membership: dn, check: dn", group);
1054 goto found;
1055 }
1056 group_ctx->value_no++;
1057 continue;
1058 }
1059
1060 /*
1061 * If the value is not a DN, and the name we were given is a dn
1062 * convert the value to a DN and do a comparison.
1063 */
1064 if (!value_is_dn && xlat_ctx->group_is_dn) {
1065 /*
1066 * So we only do the DN -> name lookup once, regardless of how many
1067 * group values we have to check, the resolved name is put in group_ctx->group_name
1068 */
1069 if (!group_ctx->group_name) {
1070 group_ctx->lookup_dn = group->vb_strvalue;
1071
1073
1074 /* Need to push this for the custom cancellation function */
1075 return unlang_function_push_with_result(p_result,
1076 request,
1078 NULL,
1081 group_ctx);
1082 }
1083
1084 if (((talloc_array_length(group_ctx->group_name) - 1) == value->bv_len) &&
1085 (memcmp(value->bv_val, group_ctx->group_name, value->bv_len) == 0)) {
1086 RDEBUG2("User found in group \"%pV\". Comparison between membership: "
1087 "name, check: name (resolved from DN \"%pV\")",
1088 fr_box_strvalue_len(value->bv_val, value->bv_len), group);
1089 goto found;
1090 }
1091 group_ctx->value_no++;
1092 continue;
1093 }
1094
1095 /*
1096 * We have a value which is a DN, and a check item which specifies the name of a group,
1097 * convert the value to a name so we can do a comparison.
1098 */
1099 if (value_is_dn && !xlat_ctx->group_is_dn) {
1100 group_ctx->lookup_dn = fr_ldap_berval_to_string(group_ctx, value);
1101 group_ctx->resolving_value = true;
1102
1104
1105 /* Need to push this for the custom cancellation function */
1106 return unlang_function_push_with_result(p_result,
1107 request,
1109 NULL,
1111 UNLANG_SUB_FRAME, group_ctx);
1112 }
1113
1114 fr_assert(0);
1115 }
1117
1118found:
1119 xlat_ctx->found = true;
1121}
1122
1123/** Ensure retrieved LDAP values are cleared up
1124 *
1125 */
1127{
1128 if (group_ctx->values) ldap_value_free_len(group_ctx->values);
1129 return 0;
1130}
1131
1132/** Query the LDAP directory to check if a user object is a member of a group
1133 *
1134 * @param[out] p_result Result of calling the module.
1135 * @param[in] request Current request.
1136 * @param[in] xlat_ctx Context of the xlat being evaluated.
1137 */
1140{
1141 rlm_ldap_t const *inst = xlat_ctx->inst;
1143
1145 talloc_set_destructor(group_ctx, userobj_dyn_free);
1146
1147 *group_ctx = (ldap_group_userobj_dyn_ctx_t) {
1148 .xlat_ctx = xlat_ctx,
1149 .attrs = { inst->group.obj_name_attr, NULL }
1150 };
1151
1152 RDEBUG2("Checking user object's %s attributes", inst->group.userobj_membership_attr);
1153
1154 /*
1155 * If a previous query was required to find the user DN, that will have
1156 * retrieved the user object membership attribute and the resulting values
1157 * can be checked.
1158 * If not then a query is needed to retrieve the user object.
1159 */
1161 request,
1162 xlat_ctx->query ? NULL : ldap_check_userobj_start,
1164 ldap_group_userobj_cancel, ~FR_SIGNAL_CANCEL,
1166 group_ctx) < 0) {
1167 talloc_free(group_ctx);
1169 }
1170
1172}
1173
1174/** Check group membership attributes to see if a user is a member.
1175 *
1176 * @param[out] p_result Result of calling the module.
1177 * @param[in] inst rlm_ldap configuration.
1178 * @param[in] request Current request.
1179 * @param[in] check vb containing the group value (name or dn).
1180 */
1182 rlm_ldap_t const *inst, request_t *request, fr_value_box_t const *check)
1183{
1184 fr_pair_t *vp;
1185 int ret;
1186 fr_dcursor_t cursor;
1187
1188 /*
1189 * We return RLM_MODULE_INVALID here as an indication
1190 * the caller should try a dynamic group lookup instead.
1191 */
1192 vp = fr_pair_dcursor_by_da_init(&cursor, &request->control_pairs, inst->group.cache_da);
1194
1195 for (vp = fr_dcursor_current(&cursor);
1196 vp;
1197 vp = fr_dcursor_next(&cursor)) {
1198 ret = fr_value_box_cmp_op(T_OP_CMP_EQ, &vp->data, check);
1199 if (ret == 1) {
1200 RDEBUG2("User found. Matched cached membership");
1202 }
1203
1204 if (ret < -1) RETURN_UNLANG_FAIL;
1205 }
1206
1207 RDEBUG2("Cached membership not found");
1208
1210}
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:472
#define RCSID(id)
Definition build.h:485
#define UNUSED
Definition build.h:317
#define NUM_ELEMENTS(_t)
Definition build.h:339
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition dcursor.h:290
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition dcursor.h:339
#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:68
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:901
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:698
static char const * null_attrs[]
Definition groups.c:39
char const * attrs[2]
For retrieving the group name.
Definition groups.c:77
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:575
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:112
ldap_group_xlat_ctx_t * xlat_ctx
Xlat context being evaluated.
Definition groups.c:76
int count
How many entries there are in values.
Definition groups.c:79
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:787
void * uctx
Optional context for use in results parsing.
Definition groups.c:69
static int userobj_dyn_free(ldap_group_userobj_dyn_ctx_t *group_ctx)
Ensure retrieved LDAP values are cleared up.
Definition groups.c:1126
char * group_name[LDAP_MAX_CACHEABLE+1]
List of group names which need resolving.
Definition groups.c:50
char * group_dn[LDAP_MAX_CACHEABLE+1]
List of group DNs which need resolving.
Definition groups.c:52
struct berval ** values
Values of the membership attribute to check.
Definition groups.c:78
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:916
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:441
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:376
tmpl_t * filter_tmpl
Tmpl to expand into LDAP filter.
Definition groups.c:65
char const * attrs[2]
For retrieving the group name.
Definition groups.c:67
rlm_ldap_t const * inst
Module instance.
Definition groups.c:45
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:1138
fr_value_box_list_t expanded_filter
Values produced by expanding filter xlat.
Definition groups.c:66
fr_value_box_t * base_dn
The base DN to search for groups in.
Definition groups.c:63
fr_ldap_thread_trunk_t * ttrunk
Trunk on which to perform additional queries.
Definition groups.c:64
fr_pair_list_t groups
Temporary list to hold pairs.
Definition groups.c:48
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:613
fr_ldap_thread_trunk_t * ttrunk
Trunk on which to perform additional queries.
Definition groups.c:47
char ** dn
Current DN being resolved.
Definition groups.c:53
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:340
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:54
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:49
bool resolving_value
Is the current query resolving a DN from values.
Definition groups.c:84
unsigned int name_cnt
How many names need resolving.
Definition groups.c:51
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:866
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:90
static void ldap_group_groupobj_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
Cancel a pending group object lookup.
Definition groups.c:594
static unlang_action_t ldap_check_groupobj_resume(unlang_result_t *p_result, request_t *request, void *uctx)
Process the results of a group object lookup.
Definition groups.c:743
fr_value_box_t * base_dn
The base DN to search for groups in.
Definition groups.c:46
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:887
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:1181
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:55
char const * lookup_dn
The DN currently being looked up, when resolving DN to name.
Definition groups.c:81
int value_no
The current entry in values being processed.
Definition groups.c:80
rlm_ldap_t const * inst
Module instance.
Definition groups.c:62
char * group_name
Result of resolving the provided group DN as to a name.
Definition groups.c:82
fr_ldap_query_t * query
Current query doing a DN to name resolution.
Definition groups.c:83
Context to use when looking up group membership using group objects.
Definition groups.c:61
Context to use when resolving group membership from the user object.
Definition groups.c:44
Context to use when evaluating group membership from the user object in an xlat.
Definition groups.c:75
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition interpret.c:1661
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
Definition interpret.c:2013
#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:134
size_t fr_ldap_util_normalise_dn(char *out, char const *in)
Normalise escape sequences in a DN.
Definition util.c:492
size_t fr_ldap_uri_escape_func(UNUSED request_t *request, char *out, size_t outlen, char const *in, UNUSED void *arg))
Converts "bad" strings into ones which are safe for LDAP.
Definition util.c:72
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:570
#define LDAP_MAX_FILTER_STR_LEN
Maximum length of an xlat expanded filter.
Definition base.h:110
bool fr_ldap_util_is_dn(char const *in, size_t inlen)
Check whether a string looks like a DN.
Definition util.c:206
LDAP * handle
libldap handle.
Definition base.h:333
fr_ldap_result_code_t ret
Result code.
Definition base.h:470
#define LDAP_MAX_CACHEABLE
Maximum number of groups we retrieve from the server for a given user which need resolving from name ...
Definition base.h:103
trunk_request_t * treq
Trunk request this query is associated with.
Definition base.h:456
char * fr_ldap_berval_to_string(TALLOC_CTX *ctx, struct berval const *in)
Convert a berval to a talloced string.
Definition util.c:443
int fr_ldap_box_escape(fr_value_box_t *vb, UNUSED void *uctx)
Definition util.c:112
fr_ldap_connection_t * ldap_conn
LDAP connection this query is running on.
Definition base.h:457
@ LDAP_RESULT_SUCCESS
Successfully got LDAP results.
Definition base.h:190
@ LDAP_RESULT_NO_RESULT
No results returned.
Definition base.h:194
@ LDAP_RESULT_BAD_DN
The requested DN does not exist.
Definition base.h:193
static int fr_ldap_berval_strncasecmp(struct berval *value, char const *str, size_t strlen)
Compare a berval with a C string of a known length using case insensitive comparison.
Definition base.h:677
LDAPMessage * result
Head of LDAP results list.
Definition base.h:468
#define LDAP_MAX_GROUP_NAME_LEN
Maximum name of a group name.
Definition base.h:108
LDAP query structure.
Definition base.h:422
Thread LDAP trunk structure.
Definition base.h:399
LDAP * fr_ldap_handle_thread_local(void)
Get a thread local dummy LDAP handle.
Definition base.c:1132
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:720
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition log.h:443
#define RWDEBUG(fmt,...)
Definition log.h:361
#define RINDENT()
Indent R* messages by one level.
Definition log.h:430
talloc_free(reap)
@ FR_TYPE_STRING
String of printable characters.
int fr_pair_value_strdup(fr_pair_t *vp, char const *src, bool tainted)
Copy data into an "string" data type.
Definition pair.c:2636
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:1342
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:287
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:2786
#define fr_assert(_expr)
Definition rad_assert.h:38
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define RDEBUG_ENABLED2()
Definition radclient.h:50
#define RDEBUG2(fmt,...)
Definition radclient.h:54
#define RDEBUG_ENABLED()
Definition radclient.h:49
#define RETURN_UNLANG_INVALID
Definition rcode.h:60
#define RETURN_UNLANG_RCODE(_rcode)
Definition rcode.h:66
#define RETURN_UNLANG_NOTFOUND
Definition rcode.h:62
#define RETURN_UNLANG_FAIL
Definition rcode.h:57
#define RETURN_UNLANG_REJECT
Definition rcode.h:56
#define RETURN_UNLANG_OK
Definition rcode.h:58
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:40
@ RLM_MODULE_INVALID
The module considers the request invalid.
Definition rcode.h:45
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:43
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:42
@ RLM_MODULE_NOTFOUND
User not found.
Definition rcode.h:47
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:48
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.
ldap_autz_call_env_t * call_env
Definition rlm_ldap.h:197
fr_ldap_thread_trunk_t * ttrunk
Definition rlm_ldap.h:196
rlm_ldap_t const * inst
Definition rlm_ldap.h:193
tmpl_t * group_filter
tmpl to expand as group membership filter.
Definition rlm_ldap.h:139
LDAPMessage * entry
Definition rlm_ldap.h:198
struct rlm_ldap_t::@174 group
fr_value_box_t group_base
Base DN in which to search for groups.
Definition rlm_ldap.h:138
char const * attrs[2]
Definition rlm_ldap.h:225
Holds state of in progress async authorization.
Definition rlm_ldap.h:191
Holds state of in progress group membership check xlat.
Definition rlm_ldap.h:219
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:335
struct tmpl_rules_s tmpl_rules_t
Definition tmpl.h:233
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:332
fr_signal_t
Signals that can be generated/processed by request signal handlers.
Definition signal.h:38
@ FR_SIGNAL_CANCEL
Request has been cancelled.
Definition signal.h:40
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:41
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
return count
Definition module.c: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
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition talloc.c:514
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:586
static int talloc_const_free(void const *ptr)
Free const'd memory.
Definition talloc.h:229
int unlang_tmpl_push(TALLOC_CTX *ctx, fr_value_box_list_t *out, request_t *request, tmpl_t const *tmpl, unlang_tmpl_args_t *args)
Push a tmpl onto the stack for evaluation.
Definition tmpl.c:254
@ TMPL_ESCAPE_PRE_CONCAT
Pre-concatenation escaping is useful for DSLs where elements of the expansion are static,...
Definition tmpl_escape.h:61
@ T_OP_CMP_EQ
Definition token.h:106
void trunk_request_signal_cancel(trunk_request_t *treq)
Cancel a trunk request.
Definition trunk.c:2151
#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:622
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:975
#define fr_box_strvalue_buffer(_val)
Definition value.h:308
#define fr_box_strvalue_len(_val, _len)
Definition value.h:305
#define fr_box_strvalue(_val)
Definition value.h:304
uintptr_t fr_value_box_safe_for_t
Escaping that's been applied to a value box.
Definition value.h:160
static TALLOC_CTX * xlat_ctx